【问题标题】:pselect notifies both pipe read end file descriptors even though only one write end was written onpselect 通知两个管道读取端文件描述符,即使只写入了一个写入端
【发布时间】:2017-06-04 05:13:59
【问题描述】:

我无法理解 pselect 的行为。基本上我所做的如下:

  • 为 SIGCHILD 信号注册一个处理程序
  • 创建两个管道
  • 使用 fork
  • 创建子进程
  • 让孩子睡 5 秒,然后退出
  • 在父进程调用pselect,等待两个管道的读取端
  • 当子进程终止时,从 SIGCHILD 处理程序内部的第一个管道中写入内容。
  • pselect 在设置了两个文件描述符的父进程中返回

我希望以下代码的输出是:

Pipe1 is set!

但是,相反,我得到:

Pipe1 is set!
Pipe2 is set!

为什么我只在一个管道写入端写入时,两个管道读取端文件描述符都设置了?这种行为是正常 pselect 虚假文件描述符通知的一部分吗?我究竟做错了什么?

这是程序:

    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include<iostream>

    enum PipeEnd {
        READ_END = 0, WRITE_END = 1, MAX_END
    };

    int pipe1[MAX_END], pipe2[MAX_END];

    void handle_sigchld(const int signal) {
        //In the SIGCHLD handler write the process ID on pipe1
        int returnStatus;
        int childPID = waitpid(static_cast<pid_t>(-1), &returnStatus, WNOHANG);
        write(pipe1[WRITE_END], &childPID, sizeof(childPID));
    }

    void createPipe(int newPipe[MAX_END]) {
        pipe(newPipe);
        fcntl(newPipe[READ_END], F_SETFL, O_NONBLOCK);
        fcntl(newPipe[WRITE_END], F_SETFL, O_NONBLOCK);
    }

    int main(int argc, const char** argv) {
        //Add a handler for the SIGCHLD signal
        struct sigaction sa;
        sa.sa_handler = &handle_sigchld;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
        sigaction(SIGCHLD, &sa, nullptr);

        //Create two pipes
        createPipe(pipe1);
        createPipe(pipe2);

        //Create a child process
        if (0 == fork()) {
            sleep(5);
            exit(0);
        }

        fd_set read_fds;
        FD_ZERO(&read_fds);
        FD_SET(pipe1[READ_END], &read_fds);
        FD_SET(pipe2[READ_END], &read_fds);
        int maxfd = std::max(pipe1[READ_END], pipe2[READ_END]);
        //Wait for a file descriptor to be notified   
        pselect(maxfd + 1, &read_fds, nullptr, nullptr, nullptr, nullptr);

        //Check if the read ends of the two pipes are set/notified
        if (FD_ISSET(pipe1[READ_END], &read_fds))
            std::cout << "Pipe1 is set!" << std::endl;
        if (FD_ISSET(pipe2[READ_END], &read_fds))
            std::cout << "Pipe2 is set!" << std::endl;
        return 0;
    }

【问题讨论】:

    标签: c process pipe fork posix


    【解决方案1】:

    即使信号处理程序没有write 任何东西,程序也会表现出相同的行为,您会感到惊讶。

    原因是pselect 失败。引用man 7 signal

    以下接口在被信号处理程序中断后永远不会重新启动,无论是否使用 SA_RESTART;当被信号处理程序中断时,它们总是失败并返回错误 EINTR:

    ....

    • 文件描述符多路复用接口:epoll_wait(2)、epoll_pwait(2)、poll(2)、ppoll(2)、select(2) 和 pselect(2)。

    始终测试系统调用返回的内容。

    【讨论】:

    • 我怀疑在 pselect 中接收到信号时会发生这种情况。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-03
    • 2013-08-11
    • 1970-01-01
    • 2019-04-13
    • 1970-01-01
    相关资源
    最近更新 更多