2016-03-16 36 views
3

請看看這個示例代碼,它使用非常好的編程模式將stdout重定向到管道。爲什麼這個簡單的代碼與`exit`一起工作,而不是`_exit`工作?

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

int main(int argc, char **argv) 
{ 
    int fd[2]; 
    pipe(fd); 

    pid_t pid = fork(); 
    if (pid == 0) { 
     close(fd[0]); 
     dup2(fd[1], 1); 
     printf("A string"); 
     _exit(0); 
    } 

    close(fd[1]); 
    char text[1000]; 
    size_t size; 
    int p = 0; 
    while ((size = read(fd[0], text+p, 1)) == 1) { 
     p++; 
    } 
    close(fd[0]); 
    text[p] = '\0'; 
    printf("%s", text); 

    return 0; 
} 

該代碼,因爲它是行不通的。正如@kaylum在評論中正確建議的,在子進程中調用exit而不是_exit會使代碼正常工作。

+0

'close(fd [1])'no,'close(fd [0])'也許?在你的代碼 – gsamaras

+0

@gsamaras不是'fd [0]'管道的讀取結束,我應該在父進程中使用從管道讀取? – Zagorax

+0

你可以關閉(fd [1]);'複製完成後,它對我來說似乎更清潔一些,但我懷疑這會改變你的程序行爲。 – jdarthenay

回答

7

exit()將所有打開的流刷新爲其終止的一部分,而_exit()不刷新 - 因此在調用_exit()時任何緩衝的輸出都會丟失。

你可以把它的「工作」之一:

1)通過調用fflush(stdout);到之前調用刷新緩衝輸出_exit()
2)禁止與標準輸出緩衝:setbuf(stdout, 0);開頭main()。

POSIX要求流由exit()沖洗:然後

出口()函數將沖洗與未寫入 緩衝數據的所有打開的流和關閉所有打開的數據流。最後,該過程將終止[CX],其結果與 過程終止的後果相同。

同樣,要求流是通過_exit()沖洗:

的_Exit()[CX]和_exit()功能不應調用使用atexit(註冊功能 )也沒有任何註冊信號處理程序。 [CX] 開放流不應刷新。是否關閉開放流 (無沖洗)是實現定義的。最後,呼叫 過程應以下述結果終止。

+0

注意:如果您生成任何有意義的輸出量,禁用「stdout」緩衝可能會顯着降低性能。真正的解決方案通常是「調用'出口'而不是」; '_exit'僅適用於非常有限的用例。 – ShadowRanger

+0

@ShadowRanger在某些情況下,'_exit()'是有用的(比如創建一個守護進程),或者當你不希望退出處理程序在分叉等時運行。那麼,理想的是什麼('exit()'或''' fflush(stdout)'+'_exit()')取決於情況。但我同意禁用緩衝是最不優選的/有用的選項(除非可能是「實時」輸出 - 否則,需要重複調​​用fflush)。 –

0

通常_被保留用於實現。 _Exit引入了C99,所以你可能不得不使用開關來使你的coompiler符合C99。

2

_exit終止該過程而不執行exit(或從main返回)的常規清理步驟。建議在某些情況下用於子進程,因爲它避免了執行不正確或不必要的清理;當分派工作人員執行短任務時,父任務可能會刷新I/O緩衝區,並且使用_exit可以防止父和子都寫入相同的緩衝數據,從而神奇地複製輸出。它還防止處理器等執行。

在C++程序中,it also bypasses stack unwinding, so RAII cleanup (destructor invocation) doesn't occur either;對於純粹的本地資源來說,這些都毫無意義(孩子快一點退出,但通常並不重要),但是如果清理影響外部可見狀態則很重要。

1

exit是一個C級函數,它可以刷新並關閉每個C級別的流(stdin,stdout和stderr是C級別的流)。

_exit是關閉每個系統級流(描述符如0,1,2等是系統級流)的POSIX級函數。在這種情況下,緩衝是系統內部關心的問題。

你已經做出了重定向讓printf(C級功能)終於寫到描述符1(系統級),但printf緩衝區而緩衝器不滿和C-字符串不包含\n(它是你的情況)。然後通過呼叫_exit終止將簡單地丟失未刷新的緩衝區內容。

在POSIX系統上,exit會刷新每個C流,然後調用_exit(這會在真正終止執行之前關閉每個系統流)。

相關問題