引言
在上一篇文章 【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 函数调用,那么流程是这样的:
- 假如当前的信号掩码为:0,说明不阻塞任何信号传递。
- 我们设置信号掩码 sigmask 为 1 (即:SIGINTR 信号),以阻塞该信号传递。
- sigprocmask(SIG_SETMASK, &sigmask, &sigsaved) 函数调用之后,sigsaved 保存的是 0,SIGINTR 信号被阻塞。
- ready = select(nfds, &readfds, &writefds, &exceptfds, timeout) 函数开始调用,期间 SIGINTR 信号是一直阻塞状态,避免 select 函数执行期间被传递,造成 select 错过该信号。
- 当 select 函数返回后,sigprocmask(SIG_SETMASK, &sigsaved, NULL) 函数调用将之前的信号掩码恢复(sigsaved 为 0),SIGINTR 阻塞状态被取消,它可以正常传递了。
- 如果 SIGINTR 在发生,select 函数就可以捕获到了。
1.4 返回值
-
0
函数执行超时,即 timeout 参数。 -
-1
函数执行出错。 -
n
就绪的描述符数量。
1.5 注意
- 相比 select 函数,pselect 是不会修改 timeout 参数的
网友评论