File I/O and Buffer Cache

1. File I/O Diagram:
 ------------------------------------------------------------------------------
        PROC                                           |
        ----                |- (7)->  minode[ ]        |
                            |       ==========         |
         cwd                |         INODE    (8)     |
        ----                |         -----            |
                            |         i_size           |     Block Device
                            |         i_block[]        |  -------------------
 (5)     ....    (6)        |         ------           |  |   blk .. blk ...| 
 fd[fd]  ---->  OenTable    |         (dev,blk)        |  -------------------
              | ---------|  |          dirty           |
              | mode=R|W |  |       ==========         |
              | minodePtr|->|                          |
              |   ....   |          BUFFER CACHE  (9)  | 
              |  offset  |         ------------        |
               ----------          | bp=(d,b) |        |
                                    -----------        |
                             (10)                      |  
                                start_io(): ================> I/O_command
                                dev_IOQueue->bp->bp <== IntHandler  
 Kernel                                                | 
 ==============================================  ^ ============================
 Uspace       -------                            |  
              |uCode |                           |
(1).          |uData : global ubuf[ ]            -----------------------
              |uHeap |                           (  (4) System Calls      )
FILE *fp=fopen( ) ==>| (2)FILE stream in Heap     read|write fd    lseek 
              |      | ==================         |       |      |     | 
              |      | int fd=open( );          -------------------------
              |      |     fbuf[BLKSIZE];  <=== fread()|fwrite|ffush|fseek
              |      |    counter, bytePtr        getchar()|putchar()|
              |      |     other status        fgets()|fputs()|fscanf|printf()
              |      |                         ------ ( I/O Library ) -------
              |uStack|: local ubuf[ ]
              -------

Legends:
(1). A proc in Umode: 
       FILE *fp=fopen("file", "r"); OR "w", etc. ===>
 
(2). fopen() creates a FILE struct in Uspace (Heap) with fbuf[BLKSIZE] ==>
     int fd = open("file", O_RDONLY); syscall to Kernel's kopen(), which
     construct an Opentable pointing to loaded INODE in memory.
     INODE's i_block[ ] points to data blks on Device.
     IF open() succeeds, fp points at the FILE struct.

(3). fread(ubuf[ ], size, nitem, fp); ==> READ nitem of size each to ubuf[ ]:
      copy data from FILE's fbuf[ ] to ubuf[ ],
           if enough, return;
      --------------------------------------------------------------
(4).  read(fd, fbuf[ ], BLKSIZE); to Kernel to read 1 BLOCK to fbuf[ ],
      then copy to ubuf[ ] again,. until enough or file has no more data.

(4). fwrite(ubuf, size, nitem, fp) ==> copy data to fbuf[ ]==> write()
     syscall to write a BLK to Kernel if fbuf[ ] is FULL.
  
     fflsuh(fp) ==> write fbuf[ ] to Kernel immediately.

    OTHER Library I/O: 
          fputc(),fgetc(),fgets(),fputs(),fscanf(),fprintf(),etc.
    ALL operate on fbuf[ ] in FILE struct.
 
==============================================================================
fread() | fwrite() ==> read() | write() syscall to Kernel

Assume read(fd, fbuf[ ], BLKSIZE)
(5). from PROC's fd[fd] --> (6) OpenTable:
                            Calculte LOGICAL block# lbk;
                            Map lbk to REAL blk via i_block[ ] (7-8)

                            int bread(dev,blk){

                         (9)    BUF *bp=getblk(dev,blk);

                                if (!bp->dataValid){
                                   start_io(bp) for READ;
                                   wait_for_DONE;
                                }
                                copy bp->data to fbuf[];
                                return BLKSIZE;
                            }
     ---------------------------------------------------------------  
     write(fd, fbuf, BLKSIZE):
       From PROC's fd[fd] --> (6) OpenTable:
                              Calculate lbk; map lbk to blk;   (7-8)
                              bwrite(dev,blk){   // DelayedWrite

                         (9)    BUF *bp = getblk(dev,blk);
 
                                copy fbuf[ ] to bp->data;
                                bp = dataVALID & DIRTY;
                                brelse(bp);
                              }     
     ----------------------------------------------------------------
(10): start_io(bp):  // bp=locked, in dev_list, opcode=R|W (ASYNC)
      {       
        1. enter bp into dev's I/O_queue -> bp1 -> bp2 -> ....  |
                                           |<-- pending I/O --->|
        2. if FIRST in I/O_queue:
             issue I/O command to device (hardware);
      }       
      ================================================================
      IntHandler()
      {  get dev from intrruptStatus; 
         bp = dequeue(first from dev.I/O_queue);
         if (bp was READ){
             bp dataVALID;
             wakeup() or V() bp->DONE; // proc wait for data will brelse(bp);
         }
         else{      //  bp was for WRITE; assume ASYNC write only 
             brelse(bp);
         }

         if (dev.I/O_queue NOT empty){  // issue I/O for first bp in queue 
            issue I/O command for first buf in dev.I/O_queue;
         }
      ================================================================

BUFFER CACHE:
   getblk(), brelse() on bp's in BUFFER CACHE to reduce physical device I/O.

Device I/O_queue: 
   Implement "disk" scheduling algorithms (linear sweep, elevator algorithm)
   to minimize "seek time".