標準I/O庫:
先來看看C標準I/O庫函數是如何用系統調用實現的。
fopen(3) :調用open(2)打開指定的文件,返回一個文件描述符(就是一個int類型的編號),分配一 個FILE結構體,其中包含該文件的描述符、I/O緩衝區和當前讀寫位置等信息,返回這 個FILE結構體的地址。
fgetc(3) :通過傳入的FILE *參數找到該文件的描述符、I/O緩衝區和當前讀寫位置,判斷能否從I/O緩衝 區中讀到下一個字符,如果能讀到就直接返回該字符,否則調用read(2),把文件描述符傳進去,讓內核讀取該文件的數據到I/O緩衝區,然後返回下一個字符。注意,對於C標準I/O庫來 說,打開的文件由FILE *指針標識,而對於內核來說,打開的文件由文件描述符標識,文件描述符從open系統調用獲得,在使用read、write、close系統調用時都需要傳文件描述符。
fputc(3) :判斷該文件的I/O緩衝區是否有空間再存放一個字符,如果有空間則直接保存在I/O緩衝區中並返回,如果I/O緩衝區已滿就調用write(2) ,讓內核把I/O緩衝區的內容寫回文件。
fclose(3) :如果I/O緩衝區中還有數據沒寫回文件,就調用write(2)寫回文件,然後調用close(2)關閉文 件,釋放FILE結構體和I/O緩衝區。
以寫文件爲例,C標準I/O庫函數(printf(3)、putchar(3)、fputs(3) )與系統調用write(2)的關 系如下圖所示。
庫函數與系統調用的層次關係
Unbuffered I/O(不帶緩衝的I/O)函數
open、read、write、close等系統函數稱爲無緩衝I/O(Unbuffered I/O)函數,因爲它們位於C標準庫的I/O緩衝區的底層。用戶程序在讀寫文件時既可以調用C標準I/O庫函數,也可以直接調用底層的Unbuffered I/O函數,那麼用哪一組函數好呢?
用Unbuffered I/O函數每次讀寫都要進內核,調一個系統調用比調一個用戶空間的函數要慢很多,所以在用戶空間開闢I/O緩衝區還是必要的,用C標準I/O庫函數就比較方便,省去了自己
管理I/O緩衝區的麻煩。 用c標準I/O庫函數要時刻注意I/O緩衝區和實際文件有可能不一致,在必要時需調 用fflush(3)。
我們知道UNIX的傳統是Everything is afile,I/O函數不僅用於讀寫常規文件,也用於讀寫設備,比如終端或網絡設備。在讀寫設備時通常是不希望有緩衝的,例如向代表網絡設備的文件寫數據就是希望數據通過網絡設備發送出去,而不希望只寫到緩衝區裏就算完事兒了,當網絡設備接收到數據時應用程序也希望第一時間被通知到,所以網絡編程通常直接調用Unbuffered
I/O函數。
C標準庫函數是C標準的一部分,而Unbuffered I/O函數是UNIX標準的一部分,在所有支持C語言的平臺上應該都可以用C標準庫函數(除了有些平臺的C編譯器沒有完全符合C標準之外),而只有在UNIX平臺上才能使用UnbufferedI/O函數,所以C標準I/O庫函數在頭文件stdio.h中聲明,而read、write等函數在頭文件unistd.h中聲明。在支持C語言的非UNIX操作系統上,標準I/O庫的底層可能由另外一組系統函數支持,例如Windows系統的底層是Win32
API,其中讀寫文件的系統 函數是ReadFile 、WriteFile。