linux c/c++ 學習總結(7)-- 標準庫中IO緩衝區

c/c++語言把輸入輸出的功能從語言分離出來,通過標準庫實現。記得在初學c語言時,書上說“stdin,stdout,stderr是c語言標準IO流,stdin負責從終端讀入,stdout,stderr負責把信息輸出到終端,其中stderr用於處理錯誤信息”,當時,看到這裏我就在想,同樣是把信息輸出到終端,什麼需要專門提供一個處理錯誤的“stderr”來?這個問題直到我學習過標準庫的IO緩衝區之後才明白……


我們平時使用的stdio.h頭文件中聲明的函數,其實是在Linux的系統調用基礎上封裝的函數,它們相對系統調用的最大區別是:它們擁有每個進程私有的緩衝區。緩存分爲3中:無緩衝,行緩衝,塊緩衝。
1. 無緩衝:使用fopen(3)函數返回的IO流,寫端只要有數據進去,讀端就能讀到。默認情況下,連接到終端的stderr是無緩衝的,無緩衝意味數據會立即顯示在終端。
2. 行緩衝:使用fopen(3)函數返回的IO流,寫端寫入的數據需要等到緩衝區填滿或者遇到換行符或者用fflush(3)強制沖洗緩衝區,讀端才能讀到。默認情況下,連接到終端的stdout是行緩衝的。
3. 塊緩衝:使用fopen(3)函數打開文件時,就採用的是塊緩衝,事先用malloc分配一塊空間,把數據先放到這塊空間上,當空間填滿後或調用fflush(3)纔會把數據寫到文件上。

看下測試代碼:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>

FILE *p = NULL;
void  fun(int n){
        fflush(p);
        fclose(p);
        exit(0);
}

int main(){

        p = fopen("./temp.txt", "w");     //fopen打開文件,默認是塊緩衝的

        //setbuf(p,NULL);                 //setbuf設置p爲無緩衝,第一次測試時先註釋掉

        signal(SIGQUIT,fun);             //安裝信號處理函數,當按下"ctrl + \"時捕獲SIGQUIT信號
        if(p == NULL){     //文件沒打開就退出
                printf("文件打開失敗\n");
                exit(0);
        }
        while(1){            //不停的向文件寫入1
                sleep(1);    //如果不sleep下,塊緩衝區很快就填滿了,看不到測試效果   
                fwrite("1",2,1,p);
        }
        fclose(p);
        return 0;
}

說明下,第一次測試時把”setbuf(p,NULL);”註釋掉,當程序運行起來後,在重新開啓一個終端,使用命令“tail -f temp.txt”觀察文件,發現什麼也沒有,原因是採用的塊緩衝,需要當緩衝區填滿後纔會真正的把數據寫入文件中;當按下“ctrl + \”時,回調信號處理函數,在處理函數中會使用fflush沖洗塊緩衝把數據寫入文件中;第二次測試時把”setbuf(p,NULL);”打開,運行程序,再次用命令“tail -f temp.txt”觀察文件,發現有數據“1”輸出。

總結:
1. 緩存區是每個進程私有的;
2. 通常我們說“標準IO是帶有緩衝區”,而linux系統調用是“不帶有緩衝區”的,其實嚴格來說系統調用也是有緩衝區,但這個緩衝區是在內核中,是進程共享的,使用write(2),read(2)等底層函數時,它們的返回值是一個int類型的文件描述符,當這個內核中緩存準備好了,我們就會說“文件描述符就緒了”。

再說個題外話:認識到內核中緩存區是很重要的,因爲對於“同步與異步”,“阻塞與非阻塞”,“爲什麼採用內存文件映射方式處理大文件會比使用read(2)效率高”等問題,不瞭解內存中緩衝區就很難理解了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章