管道不适用于信号量

时间:2015-04-14 12:22:12

标签: c pipe semaphore

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

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

2 个答案:

答案 0 :(得分:4)

您的信号量在您提供的代码中没有对您有用。我不清楚你甚至认为他们会做什么,特别是一旦你的孩子进程调用exec() - 家庭功能。

此外,一旦关闭,你不能再“打开stdout”。您可以将文件描述符1(以及stdout)与不同的文件相关联,但是一旦关闭了与其关联的所有FD,您就无法获取文件,除非open()或{{1再来一次。

然而,您似乎并不意识到在fopen()之后,子进程和父进程不共享内存,至少在一个普通内存写入的意义上是不可见的。因此,在原始代码中,当第一个孩子关闭其fork()时,父母不会受到影响,第二个孩子不会继承任何更改或需要恢复原始stdout

另一方面,请注意,当您stdout子进程,并且它继承了父进程的打开文件描述符时,会导致两个进程打开每个文件描述符。此外,当您对FD进行欺骗时,您会在同一文件上打开第二个FD。在基础打开文件关闭之前,您需要关闭它们的所有,否则可能会产生不良影响。特别是在您的情况下,您的第二个子进程挂起,因为它正在等待另一侧的fork()关闭,虽然第一个子进程关闭副本,但父进程仍然持有一个开放的FD。 stdin程序在消耗完整个输入之前不会产生任何输出。你可能会看到一些输出,如果你是exec'ing,比如wc

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

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

以下是如何设置一对通过管道单向通信的子进程的最小版本:

cat

答案 1 :(得分: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;
}