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".