首页 > 解决方案 > 为什么程序挂在孩子与父母的沟通上?

问题描述

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

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

我通过添加打印声明注意到父母或孩子都没有完成..有人可以向我解释为什么会这样吗?

为什么这不起作用?

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);
}

标签: clinuxprocesspipefork

解决方案


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

经验法则:如果您 将管道的一端连接到标准输入或标准输出,请 尽快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
$

推荐阅读