2017-05-17 30 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