C

IO 多路复用(二) pselect 函数

2020-12-17  本文已影响0人  Tubetrue01

引言

在上一篇文章 【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 参数

struct timespec
{
   time_t  tv_sec;     /* seconds */
   long    tv_nsec;    /* nanoseconds */
};

相比于 select 函数,该超时时间精确到了纳秒(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 返回值

1.5 注意


参考

上一篇下一篇

猜你喜欢

热点阅读