2017-05-17 28 views
0

以下是我嘗試瞭解如何在兩個子進程之間進行正確管道的嘗試。我只是試圖將一個Linux命令的輸出傳遞給另一個(ls到cat)並讓程序成功返回。然而,我猜測第二個分叉的孩子被卡住了,父母永遠在等着這個孩子。很長一段時間,我一直在擺弄這段代碼,試圖找出它爲什麼會陷入困境。談到C語言編程時,我很喜歡noob,但我正在努力學習。這個C代碼有什麼問題?孩子沒有回來?

有沒有人知道爲什麼程序不會退出,但掛在貓?

任何幫助將不勝感激。

謝謝。

#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <sys/wait.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
int main() 
{ 
    char *a[2] = {"/bin/ls", NULL}; 
    char *b[2] = {"/bin/cat", NULL}; 
    char *envp[2] = {getenv("PATH"), NULL}; 
    int fd[2], status; 
    pipe(fd); 
    int old_std_out = dup(1); 
    int old_std_in = dup(0); 
    dup2(fd[1], 1); 
    int pid = fork(); 
    switch(pid) 
    { 
     case -1: 
      perror("Forkscrew"); 
      exit(1); 
      break; 
     case 0: 
      execve(a[0], a, envp); 
      exit(0); 
      break; 
     default: 
      waitpid(-1, &status, 0); 
      dup2(old_std_out, 1); 
      break; 
    } 
    dup2(fd[0], 0); 
    pid = fork(); 
    switch(pid) 
    { 
     case -1: 
      perror("Forkscrew"); 
      exit(1); 
      break; 
     case 0: 
      execve(b[0], b, envp); 
      exit(0); 
      break; 
     default: 
      waitpid(-1, &status, 0); 
      dup2(old_std_in, 0); 
      break; 
    } 
    printf("\n"); 
    return 0; 
} 
+0

你是不是在跑兩隻貓?你叉,然後兩個叉再次? –

+0

如果'ls'輸出不適合管道緩衝區,你會怎麼想? – EOF

+0

@SamiKuhmonen 放兩隻貓?也許?第一個在數組a上執行exec,在數組b上執行下一個exec。我認爲fork()返回child和parent。 – user3499524

回答

2

程序中有兩種潛在的死鎖。

首先,第一子(ls)嘗試寫入到管道時可阻斷,在這種情況下waitpid()不會返回,直到ls終止,並ls不會終止,直到第二子(cat)開始執行時,其可以直到waitpid()返回纔會發生。 =>死鎖。

其次,cat將從其stdin中讀取,直到寫入結束的所有文件描述符都關閉。父進程cat都有一個寫入結束的副本,cat而不明確地知道它。如果寫入結束的唯一副本處於同一進程中(以避免此死鎖),某些操作系統將不會阻止read(),但這不能保證。無論哪種方式,由於父進程圍繞着一個filedescriptor的副本,並且父子進程waitpid()s等待管道的寫端關閉,所以再次發生死鎖。

通常情況下,簡化了程序解決了這樣的問題:

#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <sys/wait.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
int main() 
{ 
    char *a[2] = {"/bin/ls", NULL}; 
    char *b[2] = {"/bin/cat", NULL}; 
    char *envp[2] = {getenv("PATH"), NULL}; 
    int fd[2], status; 
    pipe(fd); 
    //int old_std_out = dup(1); /*No need to copy stdout...*/ 
    //int old_std_in = dup(0); /*...or stdin...*/ 
    //dup2(fd[1], 1);   /*...if you wait dup2()ing until you need to*/ 
    int pid = fork(); 
    switch(pid) 
    { 
     case -1: 
      perror("Forkscrew"); 
      exit(1); 
      //break; /*unreachable*/ 
     case 0: 
      dup2(fd[1], STDOUT_FILENO); /*NOW we dup2()*/ 
      close(fd[0]); /*no need to pass these file descriptors to...*/ 
      close(fd[1]); /*...a program that doesn't expect to have them open*/ 
      execve(a[0], a, envp); 
      exit(0); /*might want an error message*/ 
      //break; /*unreachable*/ 
     default: 
      //waitpid(-1, &status, 0); /*don't wait yet*/ 
      //dup2(old_std_out, 1); 
      close(fd[1]); /*we don't need this in the parent anymore*/ 
      break; 
    } 
    //dup2(fd[0], 0); /*not needed anymore*/ 
    pid = fork(); 
    switch(pid) 
    { 
     case -1: 
      perror("Forkscrew"); 
      /*might want to ensure the first child can terminate*/ 
      exit(1); 
      //break; /*unreachable*/ 
     case 0: 
      dup2(fd[0], STDIN_FILENO); 
      close(fd[0]); /*again, cat doesn't expect a fourth fd open*/ 
      execve(b[0], b, envp); 
      /*again, error message would be nice*/ 
      exit(0); 
      //break; 
     default: 
      //waitpid(-1, &status, 0); 
      //dup2(old_std_in, 0); 
      break; 
    } 
    waitpid(-1, &status, 0); /*don't wait until both children are created*/ 
    waitpid(-1, &status, 0); 
    printf("\n"); 
    return 0; 
} 

正如你看到的,我已經留下了一些改進建議,但是這已經現在應該做工精細,提供的execve()的工作好了。

+0

再次感謝您的幫助,爲我節省了數小時的挫折。 – user3499524