Unix 中的 C:fork、waitpid 和管道

C in Unix: fork, waitpid and pipes

我的问题是关于如何控制与管道有关的进程执行,具体是 wait / waitpid 函数的实现。

当我为以下命令 ls | head -3 创建管道时,我执行以下操作:

我的问题:based on this discussion,我需要等待child完成执行,即head -3的执行。但是 how/where 我可以用不与 close[] 命令冲突的方式实现 waitpid 功能吗?

基于this great text,描述如下:

If the parent wants to receive data from the child, it should close fd1, and the child should close fd0. If the parent wants to send data to the child, it should close fd0, and the child should close fd1. Since descriptors are shared between the parent and child, we should always be sure to close the end of pipe we aren't concerned with. On a technical note, the EOF will never be returned if the unnecessary ends of the pipe are not explicitly closed.

因此 child 将不得不等待 parent 完成管道,然后再执行。

我也看到过用管道为一个进程制作两个叉子的例子。会不会像我的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);

}

我必须承认,我在 StackExchange 上浏览过许多类似的问题。然而,几乎所有这些都具有特定的内容。我要找的是对原理和功能组合的解释。感谢您的宝贵时间!

您不能既要等待 child 又要执行。所以你需要有两个child。有两种常见的方法可以做到这一点,要么有一个 parent 进程创建两个直接的 children 和 can/must 然后等待两者;或者有一个 child 本身创建第二个,这样 parent 只需要等待一个进程终止。

选项 1(继承管道)

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...);

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

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...);