pselect通知两个管道读取端文件描述符,即使只有一个写入端被写入

pselect通知两个管道读取端文件描述符,即使只有一个写入端被写入,c,process,pipe,fork,posix,C,Process,Pipe,Fork,Posix,我无法理解pselect的行为。基本上,我所做的是: 为SIGCHILD信号注册一个处理程序 创建两个管道 使用fork创建子进程 在孩子身上睡5秒钟,然后退出 在父进程中调用pselect,等待两个管道的读取端 当子进程终止时,从SIGCHILD处理程序内部在第一个管道中写入内容 pselect在父进程中返回两个文件描述符 我希望以下代码的输出为: Pipe1 is set! 但是,我得到的是: Pipe1 is set! Pipe2 is set! 当我只在一个管道写入端写入时,为什

我无法理解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;
    }
#包括
#包括
#包括
#包括
#包括
枚举管道端{
读取结束=0,写入结束=1,最大结束
};
int pipe1[MAX_END]、pipe2[MAX_END];
无效句柄信号(常数int信号){
//在SIGCHLD处理程序中,在pipe1上写入进程ID
返回状态;
int childPID=waitpid(static_cast(-1),&returnStatus,WNOHANG);
写入(pipe1[write_END],&childPID,sizeof(childPID));
}
void createPipe(int newPipe[MAX\u END]){
管道(新管道);
fcntl(新管道[READ_END]、F_SETFL、O_Non Block);
fcntl(新管道[WRITE_END]、F_SETFL、O_NONBLOCK);
}
int main(int argc,常量字符**argv){
//为SIGCHLD信号添加处理程序
struct-sigaction-sa;
sa.sa_handler=&handle_sigchld;
sigemptyset(和sa.sa_面具);
sa.sa_flags=sa_重启| sa_停止;
SIGCHLD、sa和nullptr;
//创建两个管道
创建管道(管道1);
创建管道(管道2);
//创建子进程
如果(0==fork()){
睡眠(5);
出口(0);
}
fd_设置读取_fds;
FD_零(&read_fds);
FD_集(管道1[读取_结束],&读取_fds);
FD_集(管道2[读取_结束],&读取_fds);
int maxfd=std::max(管道1[READ_END],管道2[READ_END]);
//等待通知文件描述符
pselect(maxfd+1,&read_fds,nullptr,nullptr,nullptr,nullptr);
//检查两个管道的读取端是否设置/通知
if(FD_ISSET(管道1[读取_结束],&读取_fds))

std::cout即使信号处理程序不写任何东西,程序也会表现出相同的行为,这让您感到惊讶

原因是
pselect
失败。引用
man 7信号

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

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

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

我怀疑在pselect中收到信号时发生了类似的情况。非常感谢!