【问题标题】:execlp() failing to retrieve correct inputexeclp() 未能检索到正确的输入
【发布时间】:2018-03-24 11:02:48
【问题描述】:

我一直在尝试编写一个非常简单的程序,其中父进程通过管道将 100 行传递给子进程。然后孩子应该使用生成的行并在这些行上执行命令行程序more。 但是,当我尝试运行该程序时,它只是冻结了。我小心翼翼地关闭了所有两个进程都没有使用的描述符,但我真的不明白是什么原因造成的。

代码:

int main(void){

    int fd[2];
    if (pipe(fd) == -1){
        perror("Error creating pipe");
        return 1;
    }

    dup2(fd[1], STDOUT_FILENO);

    int i;
    for (i = 1; i <= 100; i++){
        printf("Line %d\n", i);
    }
    close(fd[1]);

    pid_t pid = fork();
    if(pid == 0) {
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);

        execlp("more", "more",(char*) NULL);
        fprintf(stderr, "Failed to execute 'more'\n");
        exit(1);
    }
    wait(NULL);
    return 0;
}

【问题讨论】:

  • 如果你在Linux 下运行你的程序strace 并看到它实际上并没有冻结,它可以正常工作并以wait() 结束。您只是看不到输出 - 请注意 write() 使用的文件描述符。
  • 我认为它“冻结”了,因为执行后提示从未出现过。但是为什么没有出现输出呢?可以做些什么来避免这种行为?

标签: c pipe fork dup


【解决方案1】:

我小心地关闭了所有两个进程都没有使用的描述符

不是真的。

dup2(fd[1], STDOUT_FILENO);

在这里,您将stdout 复制为fd[1]

close(fd[1]);

您在此处关闭fd[1],但stdout 仍处于打开状态。

那么你fork。此时,两个进程都可以通过stdout 访问管道的写入端。

    dup2(fd[0], STDIN_FILENO);
    close(fd[0]);

在子进程中,您将fd[0] 复制到stdin 并关闭fd[0]

然后,当您执行more 时,它仍然可以访问管道的两端(通过stdin / stdout)。

同时,您的父进程可以访问管道的两端(通过fd[0] / stdout)。

实际上你什么都没关闭。

还有第二个问题:你的父进程写入stdout,它绑定到管道的写入端,没有任何人读取它。取决于你写了多少,stdout 是行缓冲还是块缓冲,stdout 缓冲区有多大,以及你的管道本身可以存储多少,这本身可能会死锁。如果管道已满且周围没有人读取它,printf 将阻塞。


要解决此问题,请不要在父进程中dup2,也不要在子进程启动之前写入管道。

int main(void){
    int fd[2];
    if (pipe(fd) == -1){
        perror("Error creating pipe");
        return 1;
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("Error spawning process");
        return 2;
    }

    if (pid == 0) {
        close(fd[1]);  /* close write end of the pipe in the child */
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);

        execlp("more", "more", (char*)NULL);

        fprintf(stderr, "Failed to execute 'more'\n");
        exit(1);
    }

    close(fd[0]);  /* close read end of the pipe in the parent */

    FILE *fp = fdopen(fd[1], "w");
    if (!fp) {
        perror("Error opening file handle");
        return 3;
    }

    for (int i = 1; i <= 100; i++){
        fprintf(fp, "Line %d\n", i);
    }
    fclose(fp);  /* flush and close write end of the pipe in the parent */

    wait(NULL);
    return 0;
}

【讨论】:

  • 非常感谢您的帮助和详细的回复!那么发生了什么是“更多”的输出被重定向到管道的写入端?因此为什么我看不到输出?关于第二个问题:我不知道您所描述的内容,但我以后会尽量记住这一点!
  • @Pedro 是的,morestdout 被重定向到管道是问题的一部分,并解释了为什么您在终端上看不到任何输出。另一个问题是,因为more 本身保持其stdout 处于打开状态,它永远不会在其stdin 上看到EOF(因为它们都引用同一个管道),所以它永远不会退出。
  • 很高兴我能学到新东西!再次感谢您!
猜你喜欢
  • 2018-12-15
  • 2013-09-13
  • 1970-01-01
  • 2011-12-07
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 2015-12-13
  • 1970-01-01
相关资源
最近更新 更多