【问题标题】:Why does program hang on child to parent communication?为什么程序会挂在孩子与父母的沟通上?
【发布时间】:2019-04-04 13:34:35
【问题描述】:

我试图了解我的程序为何挂起。父母从a发送输入 它读取到子程序的文件,子程序会将其计算结果发送回它的父程序。但是,我无法通过第二个管道发送回消息。从管道读取时,父级似乎挂起。

从其他帖子中,我读到似乎表明父母应该使用waitwaitpid 等待孩子完成(在我的情况下,它们都不能解决我的问题)。

我通过添加打印语句注意到父母或孩子都没有完成..有人可以向我解释为什么会发生这种情况吗?

为什么这不起作用?

int main(int argc,char** argv) {
    char buffer[1];
    int i;

    int fd1[2]; int fd2[2];
    pipe(fd1); pipe(fd2);
    pid_t pid;

    // FIRST PROCESS.
    // -------------------
    pid = fork();
    if(pid == 0) {
        int cnt;

        dup2(fd1[0], STDIN_FILENO);
        dup2(fd2[1], STDOUT_FILENO);

        for (i = 0; i < 2; i++) {
            close(fd1[i]);
            close(fd2[i]);
        }

        while(read(STDIN_FILENO, buffer,  sizeof(buffer)) > 0) {
            fprintf(stderr, "( %s )", buffer);
            cnt = cnt + *buffer - 48;
        }

        write(STDOUT_FILENO, &cnt, sizeof(cnt));
        exit(0);
    }


    // PARENT.
    // ------------------------
    int file = open(argv[1], O_RDONLY);

    // READ THE FILE.
    while(read(file, buffer, 1) > 0) {
        if (48 <= *buffer && *buffer <= 57) {
            // PIPE TO CHILD.
            write(fd1[1], buffer, 1);
        } 
    }

    // WAIT FOR CHILD TO FINISH SENDING BACK.

    // int status = 0;
    // waitpid(pid, &status, 0);
    // THIS BLOCK DOESN'T RESOLVE ANYTHING. IT HANGS AT WAIT OR WAITPID.

    // **** THIS IS THE PART WHERE IT DOESN'T WORK.
    while(read(fd2[0], buffer, 1) > 0) {
        fprintf(stderr, "RESULT : %s", buffer);
    }

    // CLOSING PIPES
    for (i = 0; i < 2; i++) {
        close(fd1[i]);
        close(fd2[i]);
    }

    close(file);
    exit(0);
}

【问题讨论】:

  • 如何将 null-terminated 字符串放入只有单个元素的数组中?
  • 不要使用magic numbers。如果通过例如48 您的意思是 ASCII 编码字符 '0' 然后使用该字符。您可能还想了解isdigit 函数。
  • 最后,不要忘记未初始化的局部变量会有一个 indeterminate 值(可以视为随机或垃圾)。
  • 哦,还有一个“最后”的事情:当你在 child 中写 cnt 时,它将是你写的 raw binary 值,它可能不包含任何对应的字节完全可打印的字符。
  • EOF 在管道的写端关闭时发送。但是,父 子都有一个写端。所以它将保持开放。您必须在不需要它们的进程中显式关闭管道的未使用端(或 CLOEXEC,如果您打算使用 exec)。

标签: c linux process pipe fork


【解决方案1】:

您没有足够快地关闭父级中的足够文件描述符。

经验法则:如果您 dup2() 管道的一端到标准输入或标准输出,关闭两者 返回的原始文件描述符 pipe() 尽快地。 特别是,您应该在使用任何 exec*() 函数族。

如果您使用以下任一方式复制描述符,该规则也适用 dup() 或者 fcntl() F_DUPFD

现在,您的子进程完美地遵循了 RoT。但是父进程的必然结果是它们需要关闭管道的未使用端,并且它们必须关闭它们用来向该管道的读取端发出 EOF 信号的管道的写入端。这是您的代码失败的地方。

可以说,在读取文件之前,父进程应该关闭它用来写入子进程的管道的读取端,并且它应该关闭它用来从子进程读取的管道的写入端。

然后,在读取整个文件之后,它应该关闭管道的写入端到子节点,然后再进入“从子节点读取”循环。该循环永远不会终止,因为父级仍然打开管道的写入端,因此有一个进程可以(但不会)写入管道。

此外,由于子进程将整数的字节写入管道,父进程应该读取整数的字节。将char buffer[1];%s 格式一起使用是没有意义的;你需要一个字符串的空终止符,单个字符缓冲区不能同时保存空字节和任何数据。

除了各种其他改进(例如'0' 而不是48),您最终可能会得到:

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

int main(int argc, char** argv)
{
    if (argc != 2) {
        fprintf(stderr, "Usage: %s filename\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    int fd1[2];
    int fd2[2];
    char buffer[1];

    pipe(fd1);
    pipe(fd2);
    pid_t pid = fork();
    if (pid == 0) {
        int cnt = 0;

        dup2(fd1[0], STDIN_FILENO);
        dup2(fd2[1], STDOUT_FILENO);

        for (int i = 0; i < 2; i++) {
            close(fd1[i]);
            close(fd2[i]);
        }

        while (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
            fprintf(stderr, "(%c)", buffer[0]);      // Changed
            cnt = cnt + buffer[0] - '0';
        }
        putc('\n', stderr);     // Aesthetics

        write(STDOUT_FILENO, &cnt, sizeof(cnt));
        exit(0);
    }

    int file = open(argv[1], O_RDONLY);
    if (file < 0) {
        fprintf(stderr, "failed to open file '%s' for reading\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    close(fd1[0]);      // Added
    close(fd2[1]);      // Added

    while (read(file, buffer, sizeof(buffer)) > 0) {
        if ('0' <= buffer[0] && buffer[0] <= '9') {
            write(fd1[1], buffer, sizeof(buffer));
        }
    }
    close(file);        // Moved
    close(fd1[1]);      // Added

    // Rewritten
    int result;
    while (read(fd2[0], &result, sizeof(result)) == sizeof(result)) {
        fprintf(stderr, "RESULT : %d\n", result);
    }

    close(fd2[0]);      // Added

    // Close loop removed

    return 0;
}

如果它存储在文件pipe71.c 中并编译,我在运行时会得到以下输出:

$ ./pipe71 pipe71.c
(2)(0)(1)(2)(2)(2)(1)(1)(2)(0)(0)(2)(1)(0)(2)(2)(1)(0)(2)(1)(2)(0)(0)(0)(0)(0)(1)(0)(1)(1)(0)(2)(1)(0)(0)(0)(0)(9)(1)(1)(1)(1)(2)(0)(2)(0)(0)
RESULT : 49
$ ./pipe71 pipe71
(0)(0)(8)(0)(0)(2)(2)(0)(8)(1)(1)(5)(1)(1)(1)(1)(5)(1)(1)(1)(8)(5)(1)(9)(8)(5)(1)(1)(0)(4)(4)(4)(6)(0)(2)(8)(0)(0)(0)(2)(7)(1)(3)(8)(3)(0)(4)(3)(0)(4)(9)(0)(0)(0)(0)(7)(1)(9)(8)(1)(3)(0)
RESULT : 178
$

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-21
    • 2021-04-25
    • 2019-02-04
    • 2019-05-22
    • 2011-02-20
    • 2014-02-16
    • 2017-07-01
    • 1970-01-01
    相关资源
    最近更新 更多