Unix中的C:fork、waitpid和管道

Unix中的C:fork、waitpid和管道,c,unix,pipe,fork,waitpid,C,Unix,Pipe,Fork,Waitpid,我的问题是关于如何控制与管道相关的流程执行,特别是wait/waitpid函数的实现 当我为以下命令创建管道时,我执行以下操作: 我创建管道,创建子管道 对于子级,我为stdin调用dup2,执行head-3命令,关闭子级中管道的输出端 对于父级,我为stdout调用dup2,执行ls命令,关闭父级中管道的输入端 我的问题:,我确实需要等待孩子完成执行,即执行head-3。但是如何/在何处实现waitpid函数,使其不会与close[]命令冲突 基于此,描述内容如下: 如果父级希望从子级接收

我的问题是关于如何控制与管道相关的流程执行,特别是
wait
/
waitpid
函数的实现

当我为以下命令创建管道时,我执行以下操作:

  • 我创建管道,创建子管道
  • 对于子级,我为stdin调用dup2,执行
    head-3
    命令,关闭子级中管道的输出端
  • 对于父级,我为stdout调用dup2,执行
    ls
    命令,关闭父级中管道的输入端
我的问题:,我确实需要等待孩子完成执行,即执行
head-3
。但是如何/在何处实现
waitpid
函数,使其不会与
close[]
命令冲突

基于此,描述内容如下:

如果父级希望从子级接收数据,则应关闭fd1,子级应关闭fd0。如果父级希望向子级发送数据,则应关闭fd0,子级应关闭fd1。因为描述符在父级和子级之间共享,所以我们应该始终确保关闭我们不关心的管道末端。根据技术说明,如果管道的不必要端部未明确关闭,则EOF将永远不会返回

因此,在执行之前,子级必须等待父级完成管道

我也看到过一些例子,其中一个流程使用管道制作了两个叉子。这是不是为了避免僵尸进程,就像我在APUE ch.8的副本中所描述的那样

考虑以下代码实现:

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

    int main()
    {
    int pid, status;

    int fd[2];

    char *com1[2];
    char *com2[3];

    com1[0] = "ls";
    com1[1] = NULL;

    com2[0] = "head";
    com2[1] = "-3";
    com2[2] = NULL;

    pipe(fd);

    if((pid = fork()) == -1)
    {
        printf("fork error");
        exit(1);
    }

    if(pid == 0)
    {
        /* Child process closes up output side of pipe */
        dup2(fd[0], 0);
        close(fd[1]);
        execvp(com2[0], com2);
    }

    else
    {

        /* if(waitpid(0, WIFEXITED(&status), 0) != pid)
        {
            printf("wait error");
        }
        is this even needed here? */

        /* Parent process closes up input side of pipe */
        dup2(fd[1], 1);
        close(fd[0]);
        execvp(com1[0], com1);

    }

    exit(0);

}
#包括
#包括
#包括
#包括
int main()
{
int-pid,状态;
int-fd[2];
char*com1[2];
char*com2[3];
com1[0]=“ls”;
com1[1]=NULL;
com2[0]=“头部”;
com2[1]=“-3”;
com2[2]=NULL;
管道(fd);
如果((pid=fork())=-1)
{
printf(“fork错误”);
出口(1);
}
如果(pid==0)
{
/*子进程关闭管道的输出端*/
dup2(fd[0],0);
关闭(fd[1]);
execvp(com2[0],com2);
}
其他的
{
/*if(waitpid(0,WIFEXITED(&status),0)!=pid)
{
printf(“等待错误”);
}
这里需要这个吗*/
/*父进程关闭管道的输入端*/
dup2(fd[1],1);
关闭(fd[0]);
execvp(com1[0],com1);
}
出口(0);
}

我必须承认,我已经在StackExchange上浏览了许多类似的问题。然而,几乎所有这些都有特定的内容。我要找的是对原理和功能组合的解释。谢谢你抽出时间

你不能既想等孩子又想执行。所以你需要有两个孩子。有两种常见的方法可以做到这一点,要么有一个创建两个直接子级的父进程,然后可以/必须等待这两个子进程;或者让一个子进程自己创建第二个进程,这样父进程只需等待一个进程终止

选项1(继承的管道)

选项2(管道仅对相关流程可见)


如果希望父进程继续执行,则需要创建两个子进程:一个用于
ls
命令,另一个用于
head
命令。不,子级(在您的程序中)不必等待父级,如果当前没有要从管道中读取的内容,则所有非阻塞读取(默认)都将阻塞,直到有数据要读取或出现错误为止。@JoachimPileborg:“所有非阻塞读取都将阻塞”?Ehm。。。所有非阻塞读取都将阻塞我的意思是…:)“有一个自己创造第二个孩子”那么谁会等第二个呢?这和一个孩子的情况是一样的。显然,两个孩子和一个孩子的情况不同(主要的优点是父母现在可以等待……)。所有这些都应该包含在一个流程组中,更易于管理,但这是另一个故事……在第二个代码片段中,您只有一个waitpid。第二个孩子去哪里了?不需要,终止的孩子将被
initd
继承,谁将
等待它。所以你不在乎孩子是否成功终止。那么,首先为什么要运行它呢?
pipe(pi);
pid1 = fork();
if (pid1==0) { // child1
  // make redirections
  exec("head"...);
}
pid2 = fork();
if (pid2==0) { // child2
  // make redirections
  exec("ls"...);
}
// parent process
// close unuseful pipe
waitpid(pid1...);
waitpid(pid2...);
pid1 = fork();
if (pid1==0) { // child
  pipe(pi);
  pid2 = fork();
  if (pid2==0) { // gran child
    // make redirections
    exec("ls"...);
  }
  // make redirections
  exec("head"...);
}
// parent process
waitpid(pid1...);