2016-05-14 49 views
2

我正在嘗試使用管道將1個命令exec'd的stdout鏈接到另一個命令的stdin。例如模仿(cmd1 | cmd2)c在某些情況下導致程序掛起的管道

下面是我的代碼嚴重剝離版本。

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

int main (int argC, char *argv[]) 
{ 

    //Run first command 
    int fds[2]; 
    if (pipe (fds) < 0) {  //Create pipe 
     fprintf (stderr, "Pipe Failed\n"); 
    } 

    int pid; 
    if ((pid = fork()) == -1) { 
     fprintf (stderr, "Fork 1 Failed\n"); 
     exit (1); 
    } 

    if (pid == 0) {    //First child proccess 
     close (fds[0]);   //Close input end of pipe 
     dup2 (fds[1], STDOUT_FILENO); //Set stdout to output pipe 
     close (fds[1]);   //Close output end of pipe 

     fprintf (stderr, "Exec 1 executing now\n"); 
     execlp ("./addone", "./addone", NULL); //execute first command - Doesnt cause hang 
     //execlp("ls", "ls", NULL);//Causes hang 
     fprintf (stderr, "Exec 1 failed\n"); 
    } else {     //First parent segment 
     int returnStatus; 
     waitpid (pid, &returnStatus, 0);  //Wait for child 1 to finish 

     fprintf (stderr, "Back to parent 1\n"); 
    } 

    //Run second command 
    if ((pid = fork()) == -1) { 
     fprintf (stderr, "Fork 2 failed\n"); 
    } 

    if (pid == 0) {    //second child proccess 
     dup2 (fds[0], STDIN_FILENO); //Set stdin to input pipe 
     close (fds[0]);   //Close input end of pipe 
     close (fds[1]);   //Close output end of pipe 

     fprintf (stderr, "Exec 2 executing now\n"); 
     execlp ("./addone", "./addone", NULL); //execute first command - Doesnt cause hang 
     //execlp("wc", "wc", NULL);//Causes hang 
     fprintf (stderr, "Exec 2 failed\n"); 
    } else {     //second parent segment 
     int returnStatus; 
     waitpid (pid, &returnStatus, 0);  //Wait for child 2 to finish 

     fprintf (stderr, "Back to parent 2\n"); 
     //Done with pipes 
     close (fds[0]); 
     close (fds[1]); 
    } 
    return 0; 
} 

在程序中,我嘗試製作一個管道,並將第一個可執行文件標準輸出路由到秒標準輸入。嘗試使用ls |運行程序時作爲我的執行官,我的程序掛在第二任執行官上。但是,當我使用一個簡單的程序「./addone」作爲我的執行程序時,整個執行過程都會正常結束。

其中「addone」是一個小程序:

#include<stdio.h> 

int main(){ 
    int value = 0; 
    scanf("%d", &value); 
    value++; 
    printf("%d\n", value); 
} 

它有人認爲我的問題可能是有被輸入管道保持打開,但我不能工作了,其中會發生,它不沒有解釋爲什麼我的程序在使用我的簡單測試程序時運行得很好。

任何意見,什麼會導致這個掛起將不勝感激。

+1

您需要關閉父進程中的管道末端。因爲'pipe'在'fork'之前被調用,所以它在父進程和子進程中都被打開。這對你的簡單程序來說不是問題,因爲它不會象'wc'那樣循環等待'EOF'。如果管道仍然被任何進程保持打開狀態,「EOF」永遠不會到來。 – kaylum

+0

不要我已經用close(fds [0])來做到這一點;關閉(FDS [1]); 或者我應該將這些移到父級的等待聲明之前嗎? – Killedan9

+0

你確定你沒有去掉你的程序中重要的東西嗎?在你發佈的例子中,我希望它會掛在第一個waitpid,只要被執行的程序向stdout寫入任何東西 - 子節點中的printf將被阻塞,因爲從管道的另一端沒有讀數,而父母會反過來阻止等待孩子。建議:1)立即關閉父母和小孩的未使用的管子末端(即在第一次分岔後關閉父母的fds [1]等等。2)在開始第二個等待之前不要等待第一個孩子,等待兩端而不是 – davlet

回答

0

我不知道你是否解決了你的問題,但從評論中繼續,如果你打算建立一個管道,關鍵是理解如果你在一個過程中寫入管道,你必須在下一個進程中從同一管道讀取數據,如果要繼續管道,則寫入第二個管道。現在,您可以按照鏈接C Minishell Adding Pipelines所示自動執行此過程,但出於學習目的,如果您只是完成單個線性示例,則可能更容易理解。

下面是使用兩個文件描述符fd1fd2執行代碼。您的addone進程將首先從普通舊版本stdin中讀取,因此不會操作文件描述符進行讀取。但是,對於它的編寫,它必須使用寫入端(缺少更好的單詞)fd1將其輸出管道到下一個子進程。

下一個子進程必須從同一fd1輸入端讀取接收其輸入,它必須寫入到另一個文件描述符fd2輸出端。在兩個孩子退出後,父進程必須從哪裏讀取? (最後寫的管子是什麼......?哈哈!)fd2。因此,父母勉強讀端fd2(關閉所有其他未使用的結束),並閱讀最初答案多少addone添加到初始數字。

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/wait.h> 

int main (void) { 

    int fd1[2], fd2[2]; /* file descriptors 1 & 2 */ 
    int pid; 

    if (pipe (fd1) < 0 || pipe (fd2) < 0) { /* open both pipes */ 
     fprintf (stderr, "pipe creation failed\n"); 
    } 

    if ((pid = fork()) == -1) { 
     fprintf (stderr, "fork 1 failed\n"); 
     exit (1); 
    } 

    if (pid == 0) {      /* first child */ 
     dup2 (fd1[1], STDOUT_FILENO); /* dup write-end of 1st */ 
     close (fd1[0]);     /* close all others */ 
     close (fd2[0]); 
     close (fd2[1]); 

     fprintf (stderr, "Exec 1 executing now\n"); 
     execlp ("./addone", "./addone", NULL); 
     fprintf (stderr, "Exec 1 failed\n"); 
    } 
    else { 
     int returnStatus; 
     waitpid (pid, &returnStatus, 0); 
     fprintf (stderr, "Back to parent 1\n"); 
    } 

    if ((pid = fork()) == -1) { 
     fprintf (stderr, "Fork 2 failed\n"); 
    } 

    if (pid == 0) {      /* second child */ 
     dup2 (fd1[0], STDIN_FILENO); /* dup read-end of 1st */ 
     dup2 (fd2[1], STDOUT_FILENO); /* dup write-end of 2nd */ 
     close (fd1[1]);     /* close all others */ 
     close (fd2[0]); 

     fprintf (stderr, "Exec 2 executing now\n"); 
     execlp ("./addone", "./addone", NULL); 
     fprintf (stderr, "Exec 2 failed\n"); 
    } 
    else { 
     int returnStatus; 
     waitpid (pid, &returnStatus, 0); 
     fprintf (stderr, "Back to parent 2\n"); 
    } 

    dup2 (fd2[0], 0); /* dup read-end of 2nd */ 
    close (fd2[1]);  /* close all others */ 
    close (fd1[0]); 
    close (fd1[1]); 

    int total; 
    scanf ("%d", &total); 
    printf ("The total was: %d\n\n", total); 

    close (fd2[0]); 

    return 0; 
} 

示例使用/輸出

$ echo 10 | ./bin/pipeaddone 
Exec 1 executing now 
Back to parent 1 
Exec 2 executing now 
Back to parent 2 
The total was: 12 

看一下它,並讓我知道如果您有任何問題。一旦你得到它,這是有道理的。如果您暫時不使用它,不用擔心,您可以重新學習一遍:)

+0

感謝您的詳細回覆!我設法讓我的程序開始工作,並且我終於明白管道在做什麼。 – Killedan9

+0

很高興我能幫到你。自從我上次用這種方法寫了一段代碼以來,大概有一年了,所以 - 我很享受再次學習它::)'' –

相關問題