【问题标题】:Strange beahior using select() combine with pipe() and fork()使用 select() 与 pipe() 和 fork() 结合使用的奇怪行为
【发布时间】:2021-12-02 17:34:46
【问题描述】:

给定以下 sn-p:

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

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

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

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

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

有什么见解吗?

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

【问题讨论】:

  • edit您的问题并添加有关您想要实现的行为的更多详细信息。在父进程中创建管道时,fork 之后管道的两端在两个进程中都是打开的。每个进程都应该在使用管道之前关闭未使用的端。只要父级打开了写端并且没有writeread 就会阻塞。如果您在父项和子项中都关闭它(隐式退出),read 将检测到 EOF。当select 返回时,仅意味着后续的read 不会阻塞。 read 将在管道的写端被所有进程关闭时返回 0
  • @G.M.我阅读了文档的这一部分,但是 1) 多线程帐户是否也适用于多进程应用程序? 2)改变行为的不是我从子进程关闭select(读取端)使用的文件描述符,而是我从同一个父进程关闭另一个文件描述符(写入端)在调用 select 之前处理。
  • @Bodo “如果您在父子节点和子节点都关闭它(隐式退出),读取将检测到 EOF。”那是我缺少的部分!我明白现在发生了什么,感谢您的回答。

标签: c linux unix process pipe


【解决方案1】:

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多