【问题标题】:C: dup2, pipe and fork not working as expectedC:dup2,管道和叉子没有按预期工作
【发布时间】:2011-12-17 14:56:51
【问题描述】:

我正在尝试做一个简单的 fork -> 执行另一个程序 -> 对那个子进程说“你好” -> 读回一些东西 -> 打印收到的内容。

作为子程序使用的程序只是等待任何输入行并在标准输出中打印一些内容,例如“你好!”

这是我的“主机”程序(不起作用):

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

#define IN 0
#define OUT 1
#define CHILD 0

main ()
{
  pid_t pid;
  int pipefd[2];
  FILE* output;
  char buf[256];

  pipe(pipefd);

  pid = fork();    
  if (pid == CHILD)
  {
    printf("child\n");
    dup2(pipefd[IN], IN);
    dup2(pipefd[OUT], OUT);
    execl("./test", "test", (char*) NULL);
  }
  else
  {
    sleep(1);
    printf("parent\n");
    write(pipefd[IN], "hello!", 10); // write message to the process    
    read(pipefd[OUT], buf, sizeof(buf));
    printf("received: %s\n", buf);
  }
}

我明白了:

child
[.. waits 1 second ..]
parent
received: 

我错过了什么?谢谢!

编辑(test.c):

根据要求,这是子程序:

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

int getln(char line[])
{
  int nch = 0;
  int c;

  while((c = getchar()) != EOF)
    {
      if(c == '\n') break;
    line[nch] = c;
    nch++;
    }

 if(c == EOF && nch == 0) return EOF;
 return nch;
}

main()
{
  char line[20];

  getln(line);
  printf("hello there!", line);
  fflush(stdout);  
  return 0;
}

【问题讨论】:

    标签: c fork pipe


    【解决方案1】:

    您总是假设从文件描述符0 读取,并使用管道写入文件描述符1 ...您在父进程中颠倒了这种关系。对于您想要做的事情,您最终可能需要两个管道用于父子之间的双向通信,以避免父子最终读取它写入管道的内容的情况,因为进程调度是不确定的(即,如果父级也在从同一个管道读取,则不能保证子级读取父级写入管道的内容,因为父级可能只是最终写入然后读取而没有子进程交错来读取父级的内容写)。

    将您的代码更改为以下内容:

    main ()
    {
      pid_t pid;
      int pipe_to_child[2];
      int pipe_from_child[2];
      FILE* output;
      char buf[256];
    
      pipe(pipe_to_child);
      pipe(pipe_from_child);
    
      pid = fork();    
      if (pid == CHILD)
      {
        printf("child\n");
    
        //child process not using these ends of the pipe, so close them
        close(pipe_to_child[1]);
        close(pipe_from_child[0]);
    
        dup2(pipe_to_child[0], fileno(stdin));
        dup2(pipe_from_child[1], fileno(stdout));
    
        execl("./test", "test", (char*) NULL);
      }
      else
      {
        sleep(1);
        printf("parent\n");
        write(pipe_to_child[1], "hello!\n", 10); // write message to the process    
        read(pipe_from_child[0], buf, sizeof(buf));
        printf("received: %s\n", buf);
      }
    }
    

    【讨论】:

      【解决方案2】:

      为此,您需要 两个 管道:一个用于子进程的标准输入,一个用于其标准输出。您不能将管道的两端作为两个管道重复使用。

      还有父程序的这一行

      write(pipefd[IN], "hello!", 10); // write message to the process    
      

      不写换行符,所以子进程中的getln 永远不会返回。 (此外,“你好!”只有六个字符,但您正在写十个。)

      【讨论】:

        【解决方案3】:

        您可能应该使用waitwaitpid

        【讨论】:

          【解决方案4】:

          看起来您的管道描述符混淆了。调用pipe()后,pipefd[0]是管道的read端,pipefd[1]是管道的write端。您正在写入读取端,并从写入端读取。

          另外,您正在尝试为子进程的标准输入和标准输出使用一个管道。我不认为这真的是你想要做的(你需要两个管道)。

          【讨论】:

            【解决方案5】:

            看起来你的管道输入/输出是向后的——pipefd[0] 是管道的读取端,所以写入它(就像父级一样)是无意义的并且会失败。同样pipefd[1] 是写入端,因此从它读取(就像父级一样)也会失败。您应该始终检查 read 和 write 调用的返回值,看看是否有任何错误

            【讨论】:

              【解决方案6】:

              其他人说管道是单向的,这是我最初的想法。但实际上这不是我的手册页所说的:

               A read from fildes[0] accesses the data written to fildes[1]
               on  a  first-in-first-out  (FIFO)  basis  and  a  read  from
               fildes[1] accesses the data written to fildes[0] also  on  a
               FIFO basis.
              

              但是,这确实意味着如果父级正在写入pipefd[0],那么子级应该从pipefd[1] 读取,因此您将管道的错误一侧与子级的标准输入和标准输出相关联。

              从手册页来看,您似乎可以用一根管道做到这一点。但使用两个可能更清晰。

              您似乎将pipefd 的每个元素视为一个单独的管道,但事实并非如此。

              【讨论】:

              • According to the POSIX spec on pipe,“未指定 fildes[0] 是否也可写入,以及 fildes[1] 是否也可读取。”
              • 是的,如果您想要有保证的双向通道,您需要socketpair 或伪终端。
              猜你喜欢
              • 2018-04-05
              • 2018-12-19
              • 1970-01-01
              • 2015-05-04
              • 2011-07-24
              • 2020-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-10-30
              相关资源
              最近更新 更多