? ? ? ? Linux的系統I/O函數(read、write、open、close和 lseek等)與C語言的C庫函數(libc.so庫文件中)都是相對應的,它們都是動態庫函數。如下圖所示,C庫函數有fopen、fclose、fwrite、fread和fseek等。這些C庫函數都封裝在libc.so庫文件中,其中的fopen函數用于打開一個文件,其返回值為FILE *類型(指向FILE類型的一個指針),FILE類型為一個結構體,用于描述對打開的文件的一些操作,對于除了fopen以外的C庫函數都會通過這個FILE *類型指針對打開的文件進行操作。
FILE類型為一個結構體,包括三個部分:文件描述符、文件讀寫指針和I/O緩沖區。
文件描述符(整型)用于索引到對應的磁盤文件,類似于FCB(文件控制塊)和索引結點,包含了該文件在磁盤上的位置信息。對于每一個進程打開的所有文件在其PCB中都有記錄相應的文件描述符,如0代表標準輸入,其宏定義為STDIN_FILENO(#define STDIN_FILENO 0)。所有執行I/O操作的系統調用都以文件描述符,即一個非負整數來指代所打開的文件。文件描述符可以用來表示所有類型的已打開文件。同時,多個文件描述符可以指向同一個打開文件,因為有在不同進程中打開同一個文件的需求。
文件描述符是一個整型數,文件描述符表是一個整型數組,而在PCB中有一個指針,指向文件描述符表的首地址,因此根據PCB就可以找到對應的文件描述符,而每一個文件描述符都對應一個FILE *的指針,即可以根據文件描述符進一步找到FILE結構體,該結構體保存了所打開文件的屬性信息(文件大小、I/O緩沖、讀寫指針、文件打開次數等等),而這樣的結構體每個文件都只有一份,供所有打開該文件的進程共享,而這些進程所不同的只是文件描述符不一樣。FILE結構體的內容只有一份。FILE結構體類似inode,而文件描述符類似簡化的FCB。
每一個文件只有一個文件讀寫指針(讀寫文件過程中指針的實際位置),在文件讀寫時要時刻注意當前文件指針的位置。文件讀寫指針指向將要寫入或讀出的下一個地址。如果一個進程對文件正在進行寫入,此時另一個進程要讀取該文件的數據,需要利用fseek函數將文件讀寫指針置于文件的起始位置才能讀取數據。
系統調用指操作系統提供給用戶程序調用的一組接口(接口函數)來獲得內核提供的服務。在實際中程序員使用的通常不是系統調用,而應用程序接口API,也稱為系統調用編程接口,接口函數可能要一個或者幾個系統調用才能完成函數功能,此函數通過c庫(libc)實現,如fread,fopen等。
I/O緩沖區。每一個C庫函數在對文件操作時都會有對應的I/O緩沖區,I/O緩沖區屬于內存的一部分,位于用戶空間,默認大小為8KB,即8192個Bytes。
以fgetc / fputc函數為例,當用戶程序第一次調用fgetc 讀一個字節時,fgetc函數可能通過系統調用進入內核讀1K字節到I/O緩沖區中,然后返回I/O緩沖區中的第一個字節給用戶,把讀寫位置指向I/O緩沖區中的第二個字符,以后用戶再調fgetc ,就直接從I/O緩沖區中讀取,而不需要進內核了,當用戶把這1K字節都讀完之后,再次調用fgetc時,fgetc 函數會再次進入內核讀1K字節到I/O緩沖區中。C標準庫之所以會從內核預讀一些數據放 在I/O緩沖區中,是希望用戶程序隨后要用到這些數據,C標準庫的I/O緩沖區也在用戶空間,直接從用戶空間讀取數據比進內核讀數據要快得多。另一方面,用戶程序調用fputc通常只是寫到I/O緩沖區中,這樣fputc 函數可以很快地返回,如果I/O緩沖區寫滿了,fputc 就通過系統調用把I/O緩沖區中的數據傳給內核,內核最終把數據寫回磁盤或設備。有時候用戶程序希望把I/O緩沖區中的數據立刻給內核,讓內核寫回設備或磁盤,這稱為Flush操作,對應的庫函數是fflush,fclose函數在關閉文件之前也會做Flush操作。
由上可以看出,fgetc和fputc庫函數在完成工作的過程中可能需要幾個系統調用,如將I/O緩沖區的數據寫入內核緩沖,將內核緩沖數據刷到磁盤上等。因此,通過增設I/O緩沖區和內核緩沖可以減少進入內核的次數和對磁盤的操作次數(這些都需要系統調用來完成),從而提高了讀寫效率。相對于內存,機械硬盤的讀寫速度很慢。機械硬盤的讀寫尋道時間ms級,而內存的讀寫速度是ns級別。另外,從用戶空間(I/O緩沖區)直接讀寫數據也會比進入內核(系統調用)要快很多。
Flush操作。指把I/O緩沖區的數據立即傳送給內核,然后刷到磁盤上,分為強制性和非強制性兩種。C庫函數fflush是強制性把I/O緩沖區的數據立即寫給內核的緩沖區;C庫函數fsync是強制性把內核緩沖區的數據立即刷到磁盤上。main函數的return和調用main函數中的exit(退出當前進程),也會把緩沖區的數據立即寫到磁盤上。I/O緩沖區已經寫滿或者關閉文件時,將會自動將其數據寫到磁盤上。
內核緩沖區。人生三大錯覺之中的一個:在調用函數write()時,我們覺得該函數一旦返回,數據便已經寫到了文件里,可是這樣的概念僅僅是宏觀上的。實際上,操作系統實現某些文件I/O時(如磁盤文件),為了保證I/O的效率,在內核一般會用到一片專門的區域(內存或獨立的I/O地址空間)作為I/O數據緩沖區.它用在輸入輸出設備和CPU之間,用來緩存數據,使得低速的設備和快速的CPU可以協調工作避免低速的輸入輸出設備長時間占用CPU,降低系統調用,提高了CPU的工作效率。
Linux系統I/O函數是沒有I/O緩沖區的,其緩存是由用戶提供的。C庫函數都有對應的I/O緩沖區8KB(8196Bytes)。注意:內核緩沖和I/O緩沖的作用不同。