管道不适用于信号量

Pipe doesn't work with semaphores

我想在 3 个子进程之间创建两个管道,但我首先想让第一个管道 pipe1 在两个 fork 进程之间工作。问题是,当我从第一个进程复制标准输出并同样从另一个进程复制标准输入时,我没有得到输出。

int main()
{
  int pipe1[2];
  int pipe2[2];
  pipe(pipe1);
  pipe(pipe2);
  sem_t mutex_pipe1;
  sem_t mutex_pipe2;
  sem_init(&mutex_pipe1, 0, 0);
  sem_init(&mutex_pipe2, 0, 1);


  if (fork()==0) {           //process 1
    close(1);        /* close normal stdout */
    dup(pipe1[1]);   /* make stdout same as pfds[1] */
    sem_post(&mutex_pipe1);
    execlp("ls", "ls", NULL);       
  }

  if (fork()==0){           //process 2
    sem_wait(&mutex_pipe1);
    close(0);
    dup(pipe1[0]);
    dup2(pipe1[1], 1)      //want to open stdout again.
    sem_post(&mutex_pipe1);
    execlp("wc", "wc", "-l", NULL);           
  }

您的信号量在您提供的代码中没有做任何对您有用的事情。我不清楚你甚至认为他们会做什么,尤其是当你的 child 进程调用 exec() 系列函数时。

此外,一旦关闭就无法"open stdout again"。您可以将文件描述符 1(因此 stdout)与不同的文件相关联,但是一旦您关闭了与其关联的所有 FD,就无法取回文件,除非通过 open()ing 或 fopen()再试一次。

但是,您似乎没有意识到在 fork() 之后,child 和 parent 进程不共享内存,至少在普通内存写入的意义上不共享内存一个对另一个可见。因此,在您的原始代码中,当第一个 child 关闭其 stdout 时,parent 不受影响,第二个 child 不继承任何更改或任何需要恢复原来的 stdout.

另一方面,请注意,当您 fork() 一个 child 进程时,它继承了 parent 的打开文件描述符,这会导致两个进程各自拥有这些文件描述符打开。此外,当您复制一个 FD 时,您会在同一个文件上打开第二个 FD。在关闭底层打开的文件之前,您需要关闭其中的 all,否则可能会产生不良影响。特别是在您的情况下,您的第二个 child 进程挂起,因为它正在等待其 stdin 在另一侧关闭,尽管第一个 child 进程关闭 它的 复制,parent 进程上仍然持有一个打开的 FD。 wc 程序在消耗完所有输入之前不会产生输出。如果您改为执行,例如 cat.

,您可能会看到一些输出

此外,parent 通常应该等待其 child 进程完成后再退出,并且如果它继续执行其他工作,它最终必须等待它们在它们之后进行清理。

最后,总是,总是 检查 return 值的错误代码。

这是一个非常简单的版本,说明如何设置一对 child 通过管道进行单向通信的进程:

#define DIE do { perror(NULL); exit(1); } while (0)
#define DO_OR_DIE(f) do { if ((f) < 0) DIE; } while (0)

int main(void)
{
  int pipe1[2];

  DO_OR_DIE(pipe(pipe1));

  switch (fork()) {
      case -1:  /* failed to fork */
          DIE;
      case 0:   /* child process 1 */
          /* make stdout a copy of the write end of the pipe */
          DO_OR_DIE(dup2(pipe1[1], STDOUT_FILENO));
          /* close the excess file descriptor */
          DO_OR_DIE(close(pipe1[1]));
          execlp("ls", "ls", NULL);
          /* execlp returns only on failure */
          DIE;
      /* default: parent process */
  }

  /* close the parent's copy of this FD, lest child2 hang */
  DO_OR_DIE(close(pipe1[1]));

  switch (fork()) {
      case -1:  /* failed to fork */
          DIE;
      case 0:   /* child process 1 */
          /* make stdout a copy of the write end of the pipe */
          DO_OR_DIE(dup2(pipe1[0], STDIN_FILENO));
          /* close the excess file descriptor */
          DO_OR_DIE(close(pipe1[0]));
          execlp("wc", "wc", "-l", NULL);           
          /* execlp returns only on failure */
          DIE;
      /* default: parent process */
  }

  /* close the parent's copy of this FD, for consistency and tidiness*/
  DO_OR_DIE(close(pipe1[0]));

  /* wait for two child processes to stop */
  DO_OR_DIE(wait(NULL));
  DO_OR_DIE(wait(NULL));

  return 0;
}

感谢 John Bollinger 给予的启发。以下将是使用三个子进程和两个管道执行 ls|sort|less|:

的答案
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define DIE do { perror(NULL); exit(1); } while (0)
#define DO_OR_DIE(f) do { if ((f) < 0) DIE; } while (0)


int main(void)
{
  int pipe1[2];                   //pipe1
  int pipe2[2];                   //pipe2

  DO_OR_DIE(pipe(pipe1));         //initialize pipe1
  DO_OR_DIE(pipe(pipe2));         //initialize pipe2

  switch (fork()) {
      case -1:  // failed to fork 
          DIE;
      case 0:   // child process 1 
          /* make stdout a copy of the write end of pipe1 */
          DO_OR_DIE(dup2(pipe1[1], STDOUT_FILENO));
          /* close the excess file descriptor */
          DO_OR_DIE(close(pipe1[1]));
          execlp("ls", "ls", NULL);
          /* execlp returns only on failure */
          DIE;
      /* default: parent process */
  }

  /* close the parent's copy of pipe1 write end. Avoids child2 hanging */
  DO_OR_DIE(close(pipe1[1]));

  switch (fork()) {
      case -1:  // failed to fork 
          DIE;
      case 0:   // child process 2 
          /* make stdin a copy of the read end of pipe1 */
          DO_OR_DIE(dup2(pipe1[0], STDIN_FILENO));
          /* close the excess file descriptor */
          DO_OR_DIE(close(pipe1[0]));
          /* make stdout a copy of the write end of pipe2 */
          DO_OR_DIE(dup2(pipe2[1], STDOUT_FILENO));
          /* close the excess file descriptor */
          DO_OR_DIE(close(pipe2[1]));

          execlp("sort", "sort", NULL);           
          /* execlp returns only on failure */
          DIE;
      /* default: parent process */
  }

  /* close the parent's copy of pipe1 read end, for consistency and tidiness*/
  DO_OR_DIE(close(pipe1[0]));

  /* close the parent's copy of pipe2 write end. Avoids child3 hanging */
  DO_OR_DIE(close(pipe2[1]));

  switch (fork()) {
      case -1:  // failed to fork 
          DIE;
      case 0:   // child process 3 
         /* make stdin a copy of the read end of pipe2 */
             DO_OR_DIE(dup2(pipe2[0], STDIN_FILENO));
             /* close the excess file descriptor */
             DO_OR_DIE(close(pipe2[0]));          
          execlp("less", "less", NULL);           
          /* execlp returns only on failure */
          DIE;
      /* default: parent process */
  }
  /* close the parent's copy of pipe2 read end, for consistency and tidiness*/
  DO_OR_DIE(close(pipe2[0]));

  /* wait for child processes to stop */
  DO_OR_DIE(wait(NULL));
  DO_OR_DIE(wait(NULL));
  DO_OR_DIE(wait(NULL));

  return 0;
}