2015-02-05 65 views
1

我正在嘗試製作一個分叉一次的程序,而當父級等待子進程終止時,此子進程再次分叉,然後執行兩個可執行程序。程序中有一個Pipe,我已經檢查過程序中每個dup2()和pipe()的返回值 - 在這裏省略它們使它看起來更簡潔。問題是我只能得到ls -a |的結果在程序完成後排序-r。 的代碼是:在分支進程之後執行不會返回結果在程序結束之前

#include <cstdio> 
#include <cstring> 
#include <sys/wait.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <errno.h> 

int main(int argc, char *argv[]) { 
printf("Shell> \n"); fflush(stdout); 

pid_t pid1; 
pid_t pid2; 
int status = 0; 
int fd[2]; 

if(pipe(fd) < 0) { 
    printf("FATAL ERROR.\n"); 
} 

pid1 = fork(); 

if(pid1 > 0) {  // Parent 
    waitpid(pid1, &status, 0); 
    printf("\t\t------PID1 Complete-------\n\n");     
} 
else {    // Child 
    if(pid1 == 0) { 
     printf("ON CHILD\n"); 

     pid2 = fork(); 

     if(pid2 > 0) { // Child -> Parent 
      printf("ON CHILD-Parent\n"); 
      close(fd[1]); 
      dup2(fd[0], STDIN_FILENO); 
      waitpid(pid2, &status, 0); 
      printf("ON CHILD-Parent after wait\n"); 
      execlp("sort", "sort", "-r", NULL); 
      perror("Problem with execlp\n"); 
      exit(1); 
     } 
     else {   // Child -> Child 
      printf("ON CHILD->Child\n"); 
      close(fd[0]); 
      dup2(fd[1], STDOUT_FILENO); 
      execlp("ls", "ls", "-a", NULL); 
      perror("Problem with execvp\n"); 
      exit(1); 
     }    
    } // End of if(pid1 == 0) 
} // End of Child 

printf("\nEnd of program.\n"); 

return 0; 
} 

我的電流輸出爲:

Shell> 
ON CHILD 
ON CHILD-Parent 
ON CHILD->Child 
ON CHILD-Parent after wait 

我認爲這個問題是在等待,但我只是無法弄清楚如何使這項工作。有任何想法嗎?謝謝!

+0

我相信int fd [2];包含fd [0] == 0,fd [1] == 0,因爲它從不分配。然後你去關閉fd [1]並期望fd [0]工作。這可能是你的問題的原因。 – 2015-02-05 20:17:14

+0

我可以在沒有雙分叉的情況下使用同樣的想法,所以我不確定它是否真的存在問題......我認爲我的程序由於wait()而等待「永遠」。 – Xaphanius 2015-02-05 20:38:08

+1

@Robert:當'fd'指針傳遞給'pipe(fd)'時,'fd'的內容被賦值。 – nategoose 2015-02-05 20:57:45

回答

3

問題是您在祖父母流程中調用pipe。在孫進程(ls -a)退出之後,父進程(sort -r)將無限期地等待從管道讀取更多輸入,因爲某些進程(祖父進程)會在管道的寫入結束處保存一個打開的描述符。

如果關閉祖父進程中的管道描述符,或者更好地將pipe調用移入第一個分叉進程,那麼排序過程將在最後一個管道寫入結束描述符打開的進程退出時終止(DEMO):

int main() { 
    // Turn off buffering of stdout, to help with debugging 
    setvbuf(stdout, NULL, _IONBF, 0); 
    printf("Shell> \n"); 

    pid_t pid1 = fork(); 
    if(pid1 < 0) { 
     perror("fork failed"); 
    } 

    if(pid1 > 0) {  // Parent 
     int status; 
     waitpid(pid1, &status, 0); 
     printf("\t\t------PID1 Complete (%d) -------\n\n", status); 
    } else {    // Child 
     printf("ON CHILD\n"); 

     int fd[2]; 
     if(pipe(fd) < 0) { 
      perror("pipe failed"); 
      return 1; 
     } 

     pid_t pid2 = fork(); 
     if(pid2 < 0) { 
      perror("fork failed"); 
     } 

     if(pid2 > 0) { // Child -> Parent 
      printf("ON CHILD-Parent\n"); 
      close(fd[1]); 
      dup2(fd[0], STDIN_FILENO); 
      execlp("sort", "sort", "-r", NULL); 
      perror("Problem with execlp"); 
      return 1; 
     } else {   // Child -> Child 
      printf("ON CHILD->Child\n"); 
      close(fd[0]); 
      dup2(fd[1], STDOUT_FILENO); 
      execlp("ls", "ls", "-a", NULL); 
      perror("Problem with execvp"); 
      return 1; 
     } 
    } 

    printf("\nEnd of program.\n"); 
} 

的另一個問題的方案是談到了一個@nategoose:調用waitpid可能導致死鎖如果「LS -a」輸出過大,以適應管的緩衝。沒有理由等待,所以它應該簡單地被消除。

+0

工作正常!現在我將把管道創建更改爲fork之後。謝謝@凱西! – Xaphanius 2015-02-05 21:54:30

1

這不是一個真正的答案,但我有一些我想要分享的內容。

爲了確保您的輸出按照它應該的順序出現,我的沖洗量比您要多得多。請記住,當你調用功能,如fork()clone()vfork()dup()dup2()close(),或任何exec()家庭的你正在做的東西,是下面的C運行時環境,其中包括stdio功能。如果你這樣做:

printf("cat"); 
fork(); 
fflush(stdout); 

你很可能得到:

catcat 

爲您的輸出,因爲你已經複製了標準輸出結構,包括所有緩衝數據,因此除非stdio的決定,是時候無論如何在printf函數結束之前刷新,那麼每個進程的stdout緩衝區中都會有「cat」。

還有一個事實,即當您在exec系列中運行函數時數據可以保持緩衝狀態,則在用新程序替換程序之前,可能不會刷新數據。當您的程序被替換爲lssort時,標準輸出中的任何待處理數據將永遠丟失。

此外,當您使用dup時,您有另一個問題,因爲您將文件描述符從stdio下取出,因此它可能沒有被刷新,數據最終會在dup之後被刷新到新文件。

由於這些事情你應該有更多的電話fflush,但我不認爲這是你的問題在這裏。

相關問題