【问题标题】:Pipe doesn't work with semaphores管道不适用于信号量
【发布时间】:2015-04-14 12:22:12
【问题描述】:

我想在 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);           
  }

【问题讨论】:

  • 仅供参考,sem_post 都不会看到曙光。你就在他们面前execlped。该调用替换正在运行的进程映像。 IE。没有勺子。这些将执行的唯一方法是execlp 调用 failed
  • @WhozCraig 你在回答 OP 的问题 - 你应该把它作为答案,这样我就可以投票了 ;)
  • 嗯我相信我的信号量正在工作,因为在我在标准输入上调用“dup”之前,在管道上调用标准输出之前,我首先执行了“ls”并且进程挂起“wc -l”命令我想?
  • 您为什么要在其中涉及信号量?对于通过管道进行通信的进程来说,它们本质上并不是必需的。实际上,管道本身就构成了一种进程间同步机制。
  • 如果您想复制到特定的文件描述符,请始终使用dup2()。不用担心目标FD是否打开;必要时会先关闭。

标签: c pipe semaphore


【解决方案1】:

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

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

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

另一方面,请注意,当您fork() 一个子进程时,它会继承父进程的打开文件描述符,这会导致两个进程分别打开这些文件描述符。此外,当您欺骗 FD 时,您会在同一个文件上打开第二个 FD。您需要在关闭底层打开文件之前关闭它们中的所有,否则可能会产生不良影响。特别是在您的情况下,您的第二个子进程挂起,因为它正在等待其 stdin 在另一侧关闭,并且虽然第一个子进程关闭了 its 副本,但父进程仍然持有打开FD就可以了。 wc 程序在消耗完全部输入之前不会产生任何输出。如果您改为执行,例如cat,您可能会看到一些输出。

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

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

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

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

【讨论】:

  • 感谢您的 cmets 和宝贵的时间。您已经向我提供了有关重要细节的信息。
【解决方案2】:

感谢 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-12
    • 2013-02-26
    • 2020-06-11
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    相关资源
    最近更新 更多