【问题标题】:Parent process is unable to read data written by child process in pipe父进程无法读取管道中子进程写入的数据
【发布时间】:2017-09-24 18:25:54
【问题描述】:

这个论坛有很多关于管道读写的问题,但我无法解决我的问题。 下面的代码 sn-p,做了以下事情:

  1. 通过命令行参数文件名通过pipe_p传递给子进程
  2. 子进程打开指定文件,并将其内容写入 pipe_c 供父进程读取并显示在屏幕上。

一切正常,但父进程无法从管道读取数据(因为它没有打印任何内容)。 我观察到数据被子进程成功写入,因为我能够通过管道在子进程块中打印内容,但不能在父进程中。

注意:第 4 步不起作用

请大家帮帮我。

代码:

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

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

    int pipe_p[2], pipe_c[2];
    int childpid, c, k = 0;
    char buffer[1000] = {0};
    FILE *file;

    pipe(pipe_p);
    pipe(pipe_c);

    childpid = fork();

    if(childpid){
        //parent process block
        //STEP 1 -------
        close(pipe_p[0]);   //closing reading side of pipe
        write(pipe_p[1], argv[1], strlen(argv[1]));
        close(pipe_p[1]);
        //--------------
        wait(NULL);
        //--------------
        //printf("%s\n", "Its working");
        //STEP 4 -------
        close(pipe_c[1]);
        read(pipe_c[0], buffer, sizeof(buffer));
        close(pipe_c[0]);
        printf("%s\n", buffer);
        //--------------
    }
    else{
        //child process block
        //sleep(1);
        //STEP 2 -------
        close(pipe_p[1]);
        read(pipe_p[0], buffer, sizeof(buffer));
        close(pipe_p[0]);
        //printf("%s\n", buffer);
        //--------------

        //STEP 3 -------
        file = fopen(buffer, "r");
        while((c = getc(file)) != EOF){
            buffer[k++] = c;
        }
        buffer[k] = 0;
        //printf("%s", buffer);
        close(pipe_c[0]);
        write(pipe_c[1], buffer, strlen(buffer));
        close(pipe_c[1]);
        //--------------
    }

    return 0;
}

【问题讨论】:

    标签: c file pipe fork


    【解决方案1】:

    我在这段代码中发现了五个错误。我将从最不重要到最重要的顺序列出它们。我没有尝试修复任何错误,因此可能还有更多隐藏在这些错误后面。

    • 您忘记包含sys/wait.h。编译器应该抱怨 wait 的隐式声明。 (如果您的编译器没有提出任何投诉,请打开所有警告。)

    • 您没有检查是否有任何系统调用失败。 每个系统调用都应该跟一个失败检查。当一个失败时,向stderr打印失败的完整描述,包括失败的系统调用的名称、所有涉及的文件的名称(如果有的话)和strerror(errno),然后以非零值退出程序(不成功)退出代码。如果您这样做了,您自己会发现,事实上,某些事情“运作良好”。

    • 与此相关,您没有检查孩子是否退出失败。而不是wait(NULL),父级应该执行waitpid(childpid, &amp;status, 0),然后解码退出状态并将消息打印到stderr,而不是WIFEXITED(status) &amp;&amp; WEXITSTATUS(status) == 0,然后自行退出。

    • 在父级中,您在错误的位置调用 wait。在您读取并处理了来自pipe_c 的所有数据后,您需要致电wait。否则,如果子进程完全填满了管道缓冲区,程序就会死锁。 (此外,您需要从管道中读取 所有 数据,而不仅仅是前 1000 个字节。)

    • 在子节点中,缓冲区溢出。您正在从文件中将无限量的数据读入buffer,但buffer 的大小是固定的。您应该根据需要使用mallocrealloc 将其放大,或者以不大于buffer 大小的块的形式从文件复制到管道。

    我通过在-f 模式下运行strace 实用程序下的程序发现了所有这些问题(因此它会跟踪分叉的两侧),输入来自一个大文件。这是一种有价值的调试技术,您应该自己尝试一下。

    【讨论】:

    • 感谢您的建议@zwol。我没有得到你的第四点,在这里我让父进程等到子进程完成填充缓冲区(这已成功完成,你可以通过取消注释其他部分中的 printf 部分来检查)。但是在返回父上下文后,如果我将步骤 4 复制到其他部分,则不会读取在子进程中读取的数据。
    • 好的,我会检查你的编辑。如果您可以使代码正常工作,那就太好了。谢谢
    • @CunchalRoshan 修复子缓冲区溢出问题,然后使用 large 测试文件(至少 32KB),您应该会看到过早执行 wait 导致的死锁.现在,死锁被缓冲区溢出所掩盖,这使得子进程在填满管道缓冲区之前就崩溃了。
    • @CnchalRoshan 另外,如果您使用小于 1000 字节小于的文件按原样运行程序,它会按照您的预期工作(即即,父级将成功地从管道中读取数据并打印出来)。
    • 是的,我明白你关于溢出的观点,但它不适用于小于 1000 字节的文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多