在某处用三个execlp小错误创建两个管道C系统编程

Creating two pipes with three execlp small error somewhere C System Programming

我正在尝试创建三个子进程和两个将执行三个 execlp() 的管道。但是,当我的程序运行时,输出不是我所期望的。

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

int main()
{
    pid_t  pid = getpid();
    printf("STARTING PROCESSXXX %d\n",pid);

    int c1Toc2[2];
    int c2Toc3[2];

    if(pipe(c1Toc2) == -1)
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }
    if(pipe(c2Toc3) == -1)
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    int rValue = fork();

    if(rValue == -1)
    {
        perror("Child_1");
    }
    else if (rValue == 0)
    {
        printf("CHILD 1:  ");
        printf("PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n", (long) getpid(), (long) getppid(), rValue);

        dup2(c1Toc2[1], STDOUT_FILENO);
        close(c1Toc2[0]);
        close(c2Toc3[0]);

        execlp("ps", "ps", "-ef", NULL);

        exit(0);
    }

    rValue = fork();

    if(rValue == -1)
    {
        perror("Child_2");
    }
    else if (rValue == 0)
    { 
        printf("CHILD 2:  ");
        printf("PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n",
                (long) getpid(), (long) getppid(), rValue);

        dup2(c1Toc2[0], STDIN_FILENO);
        close(c1Toc2[1]);

        dup2(c2Toc3[1], STDOUT_FILENO);
        close(c2Toc3[0]);
        printf("CHILD 2 : goodbye\n");
        execlp("grep","grep","root",NULL);

        printf("CHILD 2 : goodbye\n");
        exit(0);
    }

    rValue = fork();

    if(rValue == -1)
    {
        perror("Child_3");
    }
    else if (rValue == 0)
    { 
        printf("CHILD 3:  ");
        printf("PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n",
                (long) getpid(), (long) getppid(), rValue);

        dup2(c2Toc3[0], STDIN_FILENO);
        close(c2Toc3[1]);
        close(c2Toc3[0]);
        printf("CHILD 3 : \n");
        execlp("sort","sort","-n", "-k4",NULL);

        printf("CHILD 3 : goodbye\n");
        exit(0);
    }

    close(c1Toc2[1]);
    close(c1Toc2[0]);

    close(c2Toc3[1]);
    close(c2Toc3[0]);
    // Add the code for the two children  here
    sleep(3);
    printf("PARENT: PROCESS Waiting on children to complete\n");

    printf("Final Print Statement before exit\n");
    exit(0);
}

问题是您没有关闭 children 等中的管道文件描述符,因为您将它们复制到 STDOUT_FILENO(例如 - grep child),即使程序完成了它的常规输出,它也没有完全退出并且 post 一个 EOF 到下一个 child 进程(即 - 排序),所以那些 children 挂起永远等待。

我不完全确定为什么你的 grep 进程拒绝正常退出,因为 ps child 确实退出了,这应该 post 一个 EOF 到 grep child 的标准输入。

这是一个可以满足您需求的版本:

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

int main()
{
  pid_t pid = getpid();

  printf("PARENT: STARTING PROCESSXXX %d\n",pid);

  int c1Toc2[2];
  int c2Toc3[2];

  if (pipe(c1Toc2) == -1)
  {
    perror("pipe");
    exit(EXIT_FAILURE);
  }

  if (pipe(c2Toc3) == -1)
  {
    perror("pipe");
    exit(EXIT_FAILURE);
  }

  int rValue = fork();

  if (rValue == -1)
  {
    perror("Child_1");
    exit(EXIT_FAILURE);
  }
  else if (rValue == 0)
  {
    printf("CHILD 1: PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n", (long) getpid(), (long) getppid(), rValue);

    dup2(c1Toc2[1], STDOUT_FILENO);

    close(c1Toc2[0]);
    close(c1Toc2[1]);
    close(c2Toc3[0]);
    close(c2Toc3[1]);

    execlp("ps", "ps", "-ef", NULL);
    perror("CHILD 1: execlp");
    exit(EXIT_FAILURE);
  }

  rValue = fork();

  if (rValue == -1)
  {
    perror("Child_2");
    exit(EXIT_FAILURE);
  }
  else if (rValue == 0)
  { 
    printf("CHILD 2: PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n", (long) getpid(), (long) getppid(), rValue);

    dup2(c1Toc2[0], STDIN_FILENO);
    dup2(c2Toc3[1], STDOUT_FILENO);

    close(c1Toc2[0]);
    close(c1Toc2[1]);
    close(c2Toc3[0]);
    close(c2Toc3[1]);

    execlp("grep","grep","root",NULL);
    perror("CHILD 2: execlp");
    exit(EXIT_FAILURE);
  }

  rValue = fork();

  if (rValue == -1)
  {
    perror("Child_3");
    exit(EXIT_FAILURE);
  }
  else if (rValue == 0)
  { 
    printf("CHILD 3: PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n", (long) getpid(), (long) getppid(), rValue);

    dup2(c2Toc3[0], STDIN_FILENO);

    close(c1Toc2[0]);
    close(c1Toc2[1]);
    close(c2Toc3[0]);
    close(c2Toc3[1]);

    execlp("sort","sort","-n", "-k4",NULL);
    perror("CHILD 3: execlp");
    exit(EXIT_FAILURE);
  }

  close(c1Toc2[0]);
  close(c1Toc2[1]);
  close(c2Toc3[0]);
  close(c2Toc3[1]);

  printf("PARENT: PROCESS Waiting on children to complete\n");
  sleep(3);
  printf("PARENT: Final Print Statement before exit\n");

  return 0;
}  

您的代码看起来或多或少是正确的,但它确实反映了对 fork / exec 和 child 进程管理的不完善理解。

一方面,请注意 exec-family 函数,包括 execlp(),除非失败,否则不会 return。您的代码似乎另有假设。特别是,child 进程在调用 execlp() 后不会打印 "goodbye" 消息,除非 execlp() 调用失败。你 应该 处理 execlp() 做 return 的(失败)情况,但通常你会用 error-handling / error-reporting 来处理代码。

此外,每个进程,包括 parent,都应该关闭那些它不会使用的文件描述符。这包括在将 FD 复制到标准文件编号之一后关闭它,并关闭所有专门用于其他进程的文件描述符。你错过了其中的几个:

  • Child 1 无法关闭文件描述符 c1Toc2[1](复制后)和 c2Toc3[1].
  • Child 2 无法关闭文件描述符 c1Toc2[0]c2Toc3[1](在复制它们之后)。

此外,parent进程sleep()等待它的children是没有意义的。如果要确保他们完成 and/or 以获得他们的退出状态,那么它应该使用 wait()waitpid()。如果它不打算在之后立即退出,那么它肯定应该使用其中一个函数,否则 child 进程将作为僵尸进程徘徊,直到 parent 退出。 sleep() 的数量不足以 确保 child 进程完成。另一方面,如果您不关心任何这些事情,那么您可以让 parent 退出。在这种情况下,保留它并没有什么特别的优势。

我不知道这些是否能解释您看到的意外行为,因为您还没有解释那是什么。