IO_緩衝和非緩衝

這兩天在項目原有版本的基礎上增加了一段新的協議實現代碼,因此需要和平臺進行聯調。考慮到更好地進行調試,我在代碼中添加了一段類似日誌記錄的代碼,已獲取通訊的報文內容和當時的環境參數內容,就是創建一個文件,使用標準IO的fopen、fprintf進行輸出記錄。但是在調試中,剛開始我就傻眼了,文件創建成功了,但是實時查看竟然沒有任何數據記錄。經過半天的擔驚受怕和反覆排查,發現是被標準IO的緩衝機制擺了一道,慚愧呀。。。

 

下面給出一個示例程序,模擬我的項目程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
int main() 
{
    FILE* fp=NULL;
    const char *filename_1="test_fprintf.log";
    const char *filename_2="test_write.log";
    int fd;
    fp = fopen(filename_1, "wb");
    if(fp == NULL) 
    {
        printf("open %s failed, %s\n", filename_1, strerror(errno));
        return -1;
    }
    //setbuf(fp, NULL);
    //setvbuf(fp, NULL, _IONBF, 0);
    fd = open(filename_2, O_WRONLY|O_CREAT|O_EXCL, 0666);
    if(fd < 0) 
    {
        printf("open %s failed, %s\n", filename_2, strerror(errno));
        return -1;
    }
    while(1) 
    {
        fprintf(fp, "test fprintf.\n");
        fprintf(fp, "-------test fprintf.\n");
        fprintf(fp, "=======test fprintf.\n");
        //fflush(fp);
        write(fd, "test open.\n", sizeof("test open.\n"));
        write(fd, "--------test open.\n", sizeof("--------test open.\n"));
        write(fd, "--------test open.\n", sizeof("--------test open.\n"));
        sleep(1);
    }
    return 0;
}
後臺運行上面的示例程序,然後實時查看兩個日誌文件,會發現testfrpintf.log文件一開始一直都是空的,而testwrite.log則是不斷有數據寫入,如下狀態: 

我當時就是奇怪爲什麼文件會是空的。可以看出標準IO會緩衝4096Bytes的數據,當達到這麼多數據時纔會進行實際的磁盤寫入,而系統調用write則是直接寫入,不進行緩衝。

標準IO庫提供緩衝的目的是儘可能減少使用read和write調用的次數,降低執行IO的時間,它提供三種類型的緩衝:

  1. 全緩衝。在填滿標準IO緩衝區後才進行實際IO操作,對於磁盤文件通常就是全緩衝,上面的示例就是採用緩衝。
  2. 行緩衝。在輸入和輸出中遇到換行符時進行實際的IO操作,當涉及到一個終端時,通常使用行緩衝。使用最頻繁的printf函數就是採用行緩衝,所以感覺不出緩衝的存在。
  3. 不帶緩衝。標準IO庫不對字符進行緩衝存儲。標準出錯流stderr通常是不帶緩衝的。

ISO C要求下列緩衝特徵:

  • 當且僅當標準輸入和標準輸出並不涉及交互式設備時,它們纔是全緩衝的。
  • 標準出錯決不會是全緩衝。

很多系統默認使用下列類型的緩衝:

  • 標準出錯是不帶緩衝的。
  • 如若是涉及終端設備的其它流,則他們是行緩衝的;否則是全緩衝的。

當然,對於標準IO流,我們也可以更改緩衝類型,或者是直接刷新。ISO C中提供下面兩個函數以更改緩衝類型:

1 void setbuf(FILE *fp, char *buf); //buf爲NULL,表示關閉緩衝
2 int setvbuf(FILE *fp, char *buf, int mode, size_t size); //成功返回0,出錯返回非0值

setvbuf函數中的mode參數可以爲:_IOBUF 全緩衝, _IOLBF 行緩衝, _IONBF 不帶緩衝,如果buf爲NULL, 則標準IO庫將自動地爲該流分配適當長度(常量BUFSIZ)的緩衝區。一般而言,應由系統選擇緩衝區的長度,並自動分配緩衝區,這樣關閉流時,標準IO庫將自動釋放緩衝區。

強制沖洗一個流,使用函數:

1 int fflush(FILE *fp); //成功返回0, 出錯返回EOF

項目中我是使用這個函數解決鬱悶的。

1 fflush(NULL); //沖洗所有輸出流
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章