首页 > 解决方案 > 使用 select() 与 pipe() 和 fork() 结合使用的奇怪行为

问题描述

给定以下代码段:

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>


int main() {
    int io[2];
    pipe(io);
    int read_end = io[0], write_end = io[1];

    int cpid = fork();
    
    if(cpid == 0) {
        close(read_end);
        sleep(1);
        return 0;
    } else {
        //close(write_end);
        timeval timeout = {5, 0};

        fd_set readfds;
        FD_ZERO(&readfds);
        FD_SET(read_end, &readfds);

        int ready = select(read_end + 1, &readfds, NULL, NULL, &timeout);
        printf("ready? %d", ready);
    }
}

如果片段按原样运行。程序将阻塞 5 秒。select 等待管道的读取端准备好读取一些数据,这永远不会发生并且达到超时。

但是,如果我close(writeend);在 fork 之后在父进程中取消注释,则在子进程终止后选择停止阻塞(返回“1”,而不是“-1”,这是读取端的文件描述符)。

我不明白后来的行为。我想我将不得不pselect等待管道读取端的数据或接收 SIGCHLD 的父进程,如文档中所建议:https ://man7.org/linux/man-pages/man2/ select.2.html

pselect() 系统调用允许应用程序安全地等待,直到文件描述符准备好或直到捕获到信号。

有什么见解吗?

编辑:您可以尝试使用:https ://www.programiz.com/cpp-programming/online-compiler/

标签: clinuxunixprocesspipe

解决方案


正如博多在评论中指出的那样:

如果您在父级和子级中都关闭了管道的写入端(隐式在退出时),读取将检测到 EOF。

解释为什么在父进程中关闭写端并且子进程终止时选择停止阻塞(因此关闭其管道的写端)

如果写端没有在父进程中关闭,则即使在子进程中关闭了对 read 的调用,它仍然会在父进程中阻塞,因此select会继续阻塞。

我缺少的部分是当管道的所有写端都关闭时,读取检测 EOF 并且不再阻塞。


推荐阅读