fork(2), stdio 行/塊緩衝區,子進程爲何輸出fork()之前的東西

/****************************
        > File Name: fork_stdio.c
      > Author: dulun
      > Mail: [email protected]
      > Created Time: 2016年07月26日 星期二 09時22分08秒
 **********************/

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

int main()
{
    printf(" %d hello world\n", getpid());
    write(STDOUT_FILENO, "Ciao\n", 5);

    if( -1 == fork())
    {
        exit(1);
    }

    return 0;
}

這裏寫圖片描述

如上代碼,生成輸出結果乍看令人費解。

以上輸出有兩件怪事: printf()的結果輸出行,輸出了兩次,且write()先於printf輸出。

爲什麼printf()輸出了兩次???

首先要記住,是在進程的用戶空間內存中,維護stdio緩衝區的.因此,通過fork()創建的子進程會複製這些緩衝區,當標準輸出定向到終端時,因爲缺省爲行緩衝,所以會理解顯示printf輸出的包含換行符的字符串。不過,當標準輸出重定向到文件時,由於缺省塊緩衝,所以在上述代碼中,調用fork()時,printf()輸出的字符串仍然在父進程的緩衝區中,並隨子進程的創建而產生一份副本。父,子進程調用exit()時會刷新各自stdio緩衝區,從而導致重複輸出的結果。

如何解決:

(1)針對stdio緩衝區問題的特定解決方案,可以在調用fork()之前使用函數fflush()來刷新stdio緩衝區,或使用setbuf(stdio, NULL)和setvbuf(stdio, NULL, _IONBF(無緩衝), 0) 來關閉stdio流的緩衝功能。
(2)子進程可以調用_exit(0)而非exit()以便不再刷新stdio緩衝區。
一個更爲通用的原則:在創建子進程的應用中,典型情況下僅有一個進程(一般爲父進程)應通過調用exit()終止,而其他進程應調用_exit()終止,從而保證只有一個進程調用退出程序並刷新stdio緩衝區。
有時確實希望在fork()之後刷新緩衝區,這是見機行事即可,要麼exit()要麼顯式調用fflush()之類的。
(3) write()並未輸出兩次,因爲write()會將數據傳給內核緩衝區fork()不會複製這一緩衝區。

(4) write()的輸出結果先於printf()出現,因爲write()會將數據立刻傳給高速緩存,而printf()等調用exit()刷新stdio緩衝區時才輸出。
通常,在混用stdio函數和系統調用對同一文件進行I/O操作時,需要特別謹慎

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