【问题标题】:Pipe issue: first child writes too much in to pipe管道问题:第一个孩子在管道中写入过多
【发布时间】:2017-03-28 00:00:50
【问题描述】:

我正在尝试在 C 中编写一个程序,该程序创建 2 个子进程,每个子进程都执行一个 execvp

我的问题是第一个孩子向另一个孩子读取的管道写入了太多输入。

int main(int argc, char* argv[]){

//unnamed pipe
int pipeFd[2], statusFirst,statusSecond;
pid_t childPidOne,childPidTwo;

if(pipe(pipeFd) < 0){
    perror("Pipe error:\n");
    exit(EXIT_FAILURE);
}

switch(childPidOne = fork()){
    case -1:
        perror("First Fork error:\n");
        exit(EXIT_FAILURE);
    case 0:
        printf("First child\n");
        close(pipeFd[1]);
        if( (execvp(argv[1], &argv[1])) < 0){
            perror("First execvp error:\n");
        }
        printf("End First cild\n");
        exit(0);
    default:
        //Do nothing
        break;  
}

switch(childPidTwo = fork()){
    case -1:
        perror("Second Fork error:\n");
        exit(EXIT_FAILURE);
    case 0:
        printf("Second cild\n");
        close(pipeFd[0]);
        if( (execvp(argv[3], &argv[3])) < 0){
            perror("Second execvp error:\n");
        }
        printf("End Second cild\n");
        exit(0);
    default:
        //Do nothing
        break;  
}


close(pipeFd[0]);
close(pipeFd[1]);   


if( (waitpid(childPidOne,&statusFirst,WUNTRACED | WCONTINUED)) < 0 ){
    perror("First waitpid error:\n");
}else{
    if (WIFEXITED(statusFirst)) {
       printf("First exited, status=%d\n", WEXITSTATUS(statusFirst));
    } else if (WIFSIGNALED(statusFirst)) {
       printf("First killed by signal %d\n", WTERMSIG(statusFirst));
    } else if (WIFSTOPPED(statusFirst)) {
       printf("First stopped by signal %d\n", WSTOPSIG(statusFirst));
    } else if (WIFCONTINUED(statusFirst)) {
       printf("First continued\n");
    }
}


if( (waitpid(childPidTwo,&statusSecond,WUNTRACED | WCONTINUED)) < 0 ){
    perror("Second waitpid error:\n");
}
if (WIFEXITED(statusSecond)) {
   printf("Second exited, status=%d\n", WEXITSTATUS(statusSecond));
  } else if (WIFSIGNALED(statusSecond)) {
   printf("Second killed by signal %d\n", WTERMSIG(statusSecond));
  } else if (WIFSTOPPED(statusSecond)) {
   printf("Second stopped by signal %d\n", WSTOPSIG(statusSecond));
  } else if (WIFCONTINUED(statusSecond)) {
      printf("Second continued\n");
      }

   exit(0);
return 0;
  }

也许我对 pipe + fork + execvp 的工作方式有一个错误的理解,所以让我告诉你我在我的代码中做了什么:

  1. 我创建了一个未命名的管道 - 两个孩子都使用同一个管道
  2. 我将通过分叉创建两个孩子
  3. 既然我这样执行我的程序:./pipeline [FIRST SYSTEM CALL] | [SECOND SYSTEM CALL] 或者只是给你一个例子:./pipeline echo Hello | wc -m 我关闭了管道的阅读站点
  4. 然后拨打execvp(argv[1], &amp;argv[1])

这就是错误发生的地方(我猜): 在第二个孩子完成之前,我永远不会关闭写作端,因为如果成功,execvp 将永远不会返回。

而且我知道execvp 不会关闭打开的文件描述符(可以使用fcntl 中的标志来关闭它,如What does the FD_CLOEXEC flag do? 中所述)。

示例

让我举个例子。

echo Hello | wc -m

输出结果

6

因为系统调用wc(字数)计算给定String中的字符(-m

这是正确的,因为 hello = 5 + 1(我猜是 \n\0),那就是 6。

现在,运行我的程序给出了结果

56

或获取更多信息

echo hello | wc

输出

1(行)1(单词)6(字符)

还有./pipeline echo hello | wc

输出

3(行)9(单词)56(字符)

我已经搜索了几天,但我无法弄清楚。

有什么想法吗?

非常感谢!

【问题讨论】:

  • 如果您使用./pipeline echo hello | wc 运行您的程序,那么它并没有按照您的想法执行。您的程序看到了argv[1] == echo[2] == hello没有别的。然后将程序的(组合)输出通过管道传输到wc。为了(完全)实现我认为你想做的事情,你需要转义管道字符并在你的代码中检测它(这样你就知道要传递给每个子进程的命令行部分)。
  • @TripeHound ,我通过键入手动转义字符 ./pipeline echo hello \| wc 它对我有用,除了第二个孩子以交互模式启动 wc (等待输入和 ctrl+d 标记输入结束)。这给了我 1 1 6 的正确结果。这意味着第一个孩子的输入被忽略了。有什么想法吗?

标签: c linux fork system-calls execvp


【解决方案1】:

自己解决了。

忘记使用dup2

只需在close 命令后输入dup2 命令即可。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-13
    相关资源
    最近更新 更多