【问题标题】:C programming - handling stdout and stdin using pipesC 编程 - 使用管道处理标准输出和标准输入
【发布时间】:2014-06-03 02:39:30
【问题描述】:

我正在编写一个由父母和他的孩子组成的 C 程序(使用 fork)。他们通过管道进行通信。父通过标准输出写入管道,子通过标准输入从管道读取。一旦它们连接起来,父母将“hello world”写入管道,儿子调用exec。我的代码如下所示:

int main(int argc, char *argv[])
{
 int p, a;
 char buf[1024];
 FILE *file;
 size_t nread;
 int fd[2];
 char argument[PATH_MAX];

 if(pipe(fd)<0){
 return 1;
 }

 p = fork();
 switch(p){
   case -1: perror("Error en el fork()"); return 1;
   case 0:
     close(fd[1]);
     close (0);
     dup(fd[0]);
     close(fd[0]);
     sprintf(argument,"/usr/bin/%s",argv[1]);
     execvp(argument,argv);
     perror("Error en el execv");
     exit(1);
   default: break;
 }
 close(fd[0]);
 close(1);
 a = dup(fd[1]);
 close(fd[1]);
 write(1,"Hello World\n",12);
 close(a);
 wait(NULL);
 return 0;
}

子执行的exec函数调用函数rev或wc。如果不带参数调用,rev 和 wc 应该应用于标准输入(在我的例子中是“hello world”)。但这不起作用,我不知道为什么。任何帮助将不胜感激。

【问题讨论】:

  • 父亲仍在写入1而不是写入a,而孩子没有从管道读取。

标签: c linux exec fork pipe


【解决方案1】:

这不起作用,我不知道为什么

因为您使用的是dup()。要将子进程的标准输入重定向到管道,正确使用的系统调用是dup2()

case 0:
    close( fd[1] );
    dup2( fd[0], 0 ); // this "dup"s the read-end of the pipe onto STDIN
    close( fd[0] );

请注意,您根本不需要在父代码分支中调用dup()。只需写入管道的写端:

write( fd[1], "Hello World\n", 12 );

但是,如果您还想在父分支中使用 execvp,以启动另一个程序并重定向其标准输出,那么您还必须在此处使用 dup2()

dup2( fd[1], 1 ); // this "dup"s the write-end of the pipe onto STDOUT
close( fd[1] );

阅读manpagedup2了解详情。

另外,您的代码的另一个问题是使用带有argv 作为参数列表的execvp。这将导致像revwc 这样的程序接收父程序的整个命令行,从而找到要处理的参数而不是从标准输入中读取。你可能想要

execvp( argv[1], &argv[1] );

【讨论】:

    【解决方案2】:

    您在以下代码中错误地使用了execvp()

     sprintf(argument,"/usr/bin/%s",argv[1]);
     execvp(argument,argv);
    

    如果您通过./a.out wc 运行程序,那么在这种情况下/usr/bin/wc 的参数将是./a.out wc,即./a.out 的原始参数列表,然后/usr/bin/wc 将尝试打开一个名为wc 在当前工作目录中,而不是从标准输入中读取。

    所以把上面的代码改成

     sprintf(argument,"/usr/bin/%s",argv[1]);
     execvp(argument,NULL);
    

    应该解决问题,这次/usr/bin/wc 的参数列表将是一个空列表。

    【讨论】:

      最近更新 更多