即使只写了一个写端,pselect也会通知两个管道读取结束文件描述符

时间:2017-01-19 13:24:48

标签: c process pipe fork posix

我无法理解 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;
    }

1 个答案:

答案 0 :(得分:1)

令人惊讶的是,即使信号处理程序没有write任何内容,程序也会表现出相同的行为。

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

  

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

     

...

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

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