美文网首页C
IO 多路复用(二) pselect 函数

IO 多路复用(二) pselect 函数

作者: Tubetrue01 | 来源:发表于2020-12-17 12:42 被阅读0次

    引言

    在上一篇文章 【select 函数】 中主要讲解了 select 相关的知识,本篇文章讲解它的增强版 pselect 函数。

    1.0 pselect 函数

    pselect 函数是 POSIX 发明的,如今有许多 Unix 变种支持它。(ps: 具体有哪些,还没调研)

    1.1 函数声明

    #include <sys/select.h>
    
    int pselect( int nfds, 
                 fd_set *restrict readset, 
                 fd_set *restrict writeset,
                 fd_set *restrict exceptset, 
                 const struct timespec *restrict timeout,
                 const sigset_t *restrict sigmask
               )
    

    相对于 select 函数的变化主要围绕最后两个参数展开,下面分别进行介绍。

    1.2 参数

    • timeout 超时时间
    struct timespec
    {
       time_t  tv_sec;     /* seconds */
       long    tv_nsec;    /* nanoseconds */
    };
    

    相比于 select 函数,该超时时间精确到了纳秒(select 超时时间是微妙)。

    • sigmask 信号掩码
      指定需要屏蔽的信号集,为什么会有这个参数?像 select 这一类系统调用不能很好的与信号进行交互。比如有如下场景:
    static void handler(int sig) {}
        
    int main(int argc, char *argv[])
    {
        fd_set readfds;
        struct sigaction sa;
        int nfds, ready;
    
        sa.sa_handler = handler;     /* Establish signal handler */
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sigaction(SIGINT, &sa, NULL);
        /* ... */    
        ready = select(nfds, &readfds, NULL, NULL, NULL);
        /* ... */
    

    当 select 函数返回的时候,我们就可以根据返回值以及错误码 errno 来确定到底发生了什么。如果 errno 的值等于 EINTR,我们就知道 select 函数是被信号中断的。但是这里有个问题,就是当信号在 sigaction 函数调用之后 select 函数调用之前传递过去的,那么它将无法中断 select 调用,也就是说本来由信号中断的,但是它错过了(因为发生了竞态条件)。当然,专家们也想了其他的处理手段,但是成本太大了。为此,select 的增强版本 pselect 诞生了。

    1.3 sigmask

    sigmask 参数指定了一个应该在 pselect 调用期间阻塞的信号集合,它会在调用期间覆盖当前的信号掩码,当函数返回之后在恢复之前的信号掩码。当我们做以下调用时:

    ready = pselect(nfds, &readfds, &writefds, &exceptfds, timeout, &sigmask);
    

    这就相当于内核原子性的执行以下系统调用:

    sigset_t sigsaved;
    
    sigprocmask(SIG_SETMASK, &sigmask, &sigsaved);
    ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
    sigprocmask(SIG_SETMASK, &sigsaved, NULL);
    

    我们举个例子进行说明:
    目标:我们想当 SIGINTR 信号(假如为:1)发生的时候中断 pselect 函数调用,那么流程是这样的:

    1. 假如当前的信号掩码为:0,说明不阻塞任何信号传递。
    2. 我们设置信号掩码 sigmask 为 1 (即:SIGINTR 信号),以阻塞该信号传递。
    3. sigprocmask(SIG_SETMASK, &sigmask, &sigsaved) 函数调用之后,sigsaved 保存的是 0,SIGINTR 信号被阻塞。
    4. ready = select(nfds, &readfds, &writefds, &exceptfds, timeout) 函数开始调用,期间 SIGINTR 信号是一直阻塞状态,避免 select 函数执行期间被传递,造成 select 错过该信号。
    5. 当 select 函数返回后,sigprocmask(SIG_SETMASK, &sigsaved, NULL) 函数调用将之前的信号掩码恢复(sigsaved 为 0),SIGINTR 阻塞状态被取消,它可以正常传递了。
    6. 如果 SIGINTR 在发生,select 函数就可以捕获到了。

    1.4 返回值

    • 0
      函数执行超时,即 timeout 参数。
    • -1
      函数执行出错。
    • n
      就绪的描述符数量。

    1.5 注意

    • 相比 select 函数,pselect 是不会修改 timeout 参数的

    参考

    相关文章

      网友评论

        本文标题:IO 多路复用(二) pselect 函数

        本文链接:https://www.haomeiwen.com/subject/mqwngktx.html