为什么read/write不如fget/fput快

预读入缓输出机制:

  • 磁盘→内核缓冲区→用户缓冲区→应用程序

  • 应用程序→用户缓冲区→内核缓冲区→磁盘

read/write函数称为unbuffered IO,即无用户级缓冲区(蓝色方框),但是也许会使用内核缓冲区。使用read/write时手动指定缓冲区大小,假如缓冲区设置为1字节就会一个字节一个字节地读写。从用户态切换到内核态需要较多耗时,如果只写一个字节,就会效率低下。

用fputc并非一次写一个字节,而是函数库自己有一个缓冲。先在用户态把这个缓冲填满,再切换到内核态写入。

因此,如果write函数缓冲区太小,就会花费大量时间切换用户态和内核态。假如在写程序时把read/write的缓冲区设置大一点,就相当于自己设置了用户级缓冲区,会比原来更快。

结论:能使用库函数优先用库函数,自己用系统调用不一定比库函数好。

另:写入磁盘是一个物理操作,假如来一个数据写一个数据就会效率低下。因此内核有一个系统级缓冲,默认4096字节。缓冲满了才一次性写入磁盘。

另:openreadwriteclose等系統函數稱為無緩衝I/O(Unbuffered I/O)函數,因為它們位於C標準庫的I/O緩衝區的底層[36]。用戶程序在讀寫檔案時既可以調用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 a file,I/O函數不僅用於讀寫常規檔案,也用於讀寫設備,比如終端或網絡設備。在讀寫設備時通常是不希望有緩衝的,例如向代表網絡設備的檔案寫數據就是希望數據通過網絡設備發送出去,而不希望只寫到緩衝區裡就算完事兒了,當網絡設備接收到數據時應用程序也希望第一時間被通知到,所以網絡編程通常直接調用Unbuffered I/O函數。

C標準庫函數是C標準的一部分,而Unbuffered I/O函數是UNIX標準的一部分,在所有支持C語言的平台上應該都可以用C標準庫函數(除了有些平台的C編譯器沒有完全符合C標準之外),而只有在UNIX平台上才能使用Unbuffered I/O函數,所以C標準I/O庫函數在標頭檔stdio.h中聲明,而readwrite等函數在標頭檔unistd.h中聲明。在支持C語言的非UNIX操作系統上,標準I/O庫的底層可能由另外一組系統函數支持,例如Windows系統的底層是Win32 API,其中讀寫檔案的系統函數是ReadFileWriteFile

shihyu.github.io/books/ch28s02.html