2013-05-04 77 views
1

我正在編寫一個程序,它需要同時雙向與外部程序進行通信,即同時讀取和寫入外部程序。分叉後退出()或_exit()?

我創建了兩個管道,一個用於將數據發送到外部進程,另一個用於從外部進程接收數據。在分解成外部程序的子進程之後,父進程再次分叉。新的孩子現在將數據寫入到外部程序的輸出管道中,父母現在從外部程序的輸入管道中讀取數據以供進一步處理。我聽說使用exit(3)可能會導致緩衝區被刷新兩次,但是我也擔心使用_exit(2)可能會留下緩衝區,使其不能被刷新。在我的程序中,分叉之前和之後都有輸出。我應該在這種情況下使用哪個(exit)(3)或_exit(2)?

以下是我的主要功能。爲簡單起見,#includes和輔助功能被省略。

int main() { 
    srand(time(NULL)); 
    ssize_t n; 
    cin >> n; 
    for (double p = 0.0; p <= 1.0; p += 0.1) { 
     string s = generate(n, p); 
     int out_fd[2]; 
     int in_fd[2]; 
     pipe(out_fd); 
     pipe(in_fd); 
     pid_t child = fork(); 
     if (child) { 
      // parent 
      close(out_fd[0]); 
      close(in_fd[1]); 
      if (fork()) { 
       close(out_fd[1]); 
       ssize_t size = 0; 
       const ssize_t block_size = 1048576; 
       char buf[block_size]; 
       ssize_t n_read; 
       while ((n_read = read(in_fd[0], buf, block_size)) != 0) { 
        size += n_read; 
       } 
       size += n_read; 
       close(in_fd[0]); 
       cout << "p = " << p << "; compress ratio = " << double(size)/double(n) << '\n'; // data written before forking (the loop continues to fork) 
      } else { 
       write(out_fd[1], s.data(), s.size()); // data written after forking 
       exit(EXIT_SUCCESS); // exit(3) or _exit(2) ? 
      } 
     } else { 
      // child 
      close(in_fd[0]); 
      close(out_fd[1]); 
      dup2(out_fd[0], STDIN_FILENO); 
      dup2(in_fd[1], STDOUT_FILENO); 
      close(STDERR_FILENO); 
      execlp("xz", "xz", "-9", "--format=raw", reinterpret_cast<char *>(NULL)); 
     } 
    } 
} 
+1

可能重複[如何退出子進程 - \ _exit()與退出](http://stackoverflow.com/questions/2329640/how-to-exit-a-child-process-exit -vs-exit) – hvd 2013-05-04 07:51:27

+0

@ hvd:不,我不這麼認爲。鏈接的副本是關於處理錯誤的。這種情況似乎不是關於錯誤(雖然在子進程中的錯誤處理似乎有點缺乏...... – 2013-05-04 07:55:47

+0

@MatsPetersson我不明白這個問題是如何處理錯誤的,你能詳細說明一下嗎?速度,我認爲在那裏接受的答案中提出的觀點適用於這裏 – hvd 2013-05-04 07:59:51

回答

1

你需要小心這些事情。 exit()做不同的事情_exit()並沒有_Exit()又不同,作爲答案建議作爲duplicate解釋的那樣,_Exit(不一樣_exit,注意大寫字母E)將不會調用atexit()處理程序,或刷新任何輸出緩衝器,刪除臨時文件等[其實可能是atexit()處理,但也可以直接調用,具體取決於C庫代碼的寫法]。

您的大部分輸出都是通過write完成的,應從應用程序角度來看,這些輸出不應該緩衝。但你也打電話cout << ...。您需要確保在退出前刷新。現在,您正在使用'\n'作爲行標記的末尾,這可能會或可能不會刷新輸出。如果您將其更改爲endl,則會刷新該文件。現在,您可以從輸出的角度安全地使用_Exit() - 例如,如果您的代碼要設置自己的atexit()處理程序,例如打開臨時文件或一堆其他類似的東西,這會有問題。如果你想在分叉過程中做更復雜的事情,應該由另一個exec完成。

在您的程序中,沒有任何掛起的輸出要刷新,因此它「起作用」,但是如果您在代碼的開頭添加了cout << ... << '\n';(或不帶換行符)type語句,會發現它出錯了。如果您添加一個cout.flush();,它將「修復」問題(根據您當前的代碼)。

你也應該從execlp()通話檢查返回值,在這種情況下調用_Exit()(和主要工藝處理它,所以你不要繼續在出現故障的情況下,循環?)

+0

我的代碼僅供內部使用(它不是應用程序代碼),所以爲了簡單起見,我省去了錯誤檢查。但是,對於我的代碼來說,輸出完全是我打算這樣做的事情至關重要,因爲它會被外部程序解析。 – 2013-05-05 09:12:36

1

在孩子fork()的分支,使用exit()通常是不正確的,因爲這會導致stdio緩衝區被刷新兩次,並且臨時文件被意外刪除。在C++代碼中,情況更糟,因爲靜態對象的析構函數可能運行不正確。 (有一些不尋常的情況,比如守護進程,父進程應該調用_exit()而不是子進程;基本規則適用於絕大多數情況,即每次進入主進程只應調用一次exit() )