POSIX的pselect函数

参考:《UNIX 网络编程 · 卷1 : 套接字联网API》

pselect 函数

pselect 函数是由 POSIX 发明的,如今许多 Unix 变种都支持它。pselect 函数原型:

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

pselect 相对于通常的 select 有两个区别

  1. pselect 使用 timespec 结构,而不使用 timeval 结构。timespec 结构是 POSIX 的又一个发明。

    struct timespec
    {
        time_t tv_sec; //秒
        long    tv_nsec; //纳秒
    };
    

    这两个结构的区别在于第二个成员:timespec的成员 tv_nsec 指定纳秒数,而 timeval 的该成员 tv_usec 指定微秒数。

  2. pselect 函数增加了第六个参数:一个指向信号掩码的指针。该参数允许程序先禁止递交某些信号,再测试由这些当前被禁止的信号处理函数设置的全局变量,然后调用 pselect,告诉它重新设置信号掩码。

    考虑下面的例子,这个程序的 SIGINT 信号处理函数仅仅设置全局变量 intr_flag 并返回。如果我们的进程阻塞于select 调用,那么从信号处理函数的返回将导致 select 返回 EINTR 错误。然而调用 select 时,代码看起来大体如下:

    if( intr_flag )
        handle_intr();
    
    if( (nready = select(...)) < 0 )
    {
        if( errno == EINTR )
        {
            if( intr_flag )
            handle_intr();
        }
        ...
    }
    

    问题是在测试 intr_flag 和调用 select 之间如果有信号发生,那么要是 select 永远阻塞,该信号就会丢失。

    有了 pselect 后,我们可以如下可靠地编写这个例子的代码:

    sigset_t newmask, oldmask, zeromask;
    sigemptyset(&zeromask);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGINT);
    sigprocmask(SIG_BLOCK, &newmask, &oldmask); //block SIGINT
    
    if(intr_flag)
        handle_intr();
     
    if( (nready = pselect(..., &zeromask)) < 0 )
    {
        if(errno == EINTR)
        {
            if(intr_flag)
                handle_intr();
        }
        ...
    }
    

在测试 intr_flag 变量之前,我们阻塞 SIGINT。当 pselect 被调用时,它先以空集(zeromask)取代进程的信号掩码,再检查描述字,并可能进入睡眠。然而当 pselect 函数返回时,进程的信号掩码又被重置为调用 pselect 之前的值(即SIGINT被阻塞),也就是说只有在 pslecet 中,SIGINT 是不被阻塞的。

注意:pselect(…, &zeromask) 与 pselect(…, NULL) 意义完全不同,前者是在调用期间不阻塞任何新信号,后者和调用 select 相同,保持原来的信号 mask。

有 sigmask 的时候,pselect 相当于如下的 select() 函数,在进入 select() 函数之前手动将信号的掩码改变,并保存之前的掩码值。select() 函数执行之后,再恢复为之前的信号掩码值。

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

示例说明

  1. 用 pselect 屏蔽信号
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#define BUFFSIZE 80
void sig_int(int signo);
void err_sys(const char *p_error);

void sig_alrm(int signo)
{
    char s[] = "receive";
    psignal(signo, s);
    return;
}

int main(int argc, char **argv)
{
    int maxfdp1;
    fd_set rset;
    sigset_t sigmask;
    ssize_t nread;
    char buf[BUFFSIZE];
    sigset_t sigset;
    struct sigaction act;
    //set SIGALRM signal handler
    act.sa_handler = sig_alrm;
    if (sigemptyset(&act.sa_mask) == -1)
        err_sys("sigemptyset");
    act.sa_flags = 0;
    if (sigaction(SIGALRM, &act, NULL) == -1)
        err_sys("sigaction");
    //initialize signal set and addition SIGALRM into sigset
    if (sigemptyset(&sigset) == -1)
        err_sys("sigemptyet");
    if (sigaddset(&sigset, SIGALRM) == -1)
        err_sys("sigaddset");
    alarm(1);
    FD_ZERO(&rset);
    FD_SET(STDIN_FILENO, &rset);
    maxfdp1 = STDIN_FILENO + 1;
    if (pselect(maxfdp1, &rset, NULL, NULL, NULL, &sigset) <= 0)
        //if (select(maxfdp1, &rset, NULL, NULL, NULL) <= 0)
        err_sys("pselect error");
    if (FD_ISSET(STDIN_FILENO, &rset))
    {
        if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) == -1)
            err_sys("read error");
        if (write(STDOUT_FILENO, buf, nread) != nread)
            err_sys("write error");
    }
    exit(0);
}

void sig_int(int signo)
{
    char s[] = "received";
    psignal(signo, s);
    return;
}

void err_sys(const char *p_error)
{
    perror(p_error);
    exit(1);
}

上段代码如果没有 CTRL+C 送上一个 SIGINT 信号,将永远阻塞在与用户的交互上,ALARM 产生的 SIGALRM 信号永远打断不了 PSELECT,ALARM 信号被成功屏蔽。

  1. 用 sigprocmask 屏蔽信号
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#define BUFFSIZE 80
void sig_int(int signo);
void err_sys(const char *p_error);

void sig_alrm(int signo)
{
    char s[] = "receive";
    psignal(signo, s);
    return;
}

int main(int argc, char **argv)
{
    int maxfdp1;
    fd_set rset;
    sigset_t zeromask;
    ssize_t nread;
    char buf[BUFFSIZE];
    sigset_t sigset;
    struct sigaction act;
    //set SIGALRM signal handler
    act.sa_handler = sig_alrm;
    if (sigemptyset(&act.sa_mask) == -1)
        err_sys("sigemptyset");
    if (sigemptyset(&zeromask) == -1)
        err_sys("sigemptyset");
    act.sa_flags = 0;
    if (sigaction(SIGALRM, &act, NULL) == -1)
        err_sys("sigaction");
    //initialize signal set and addition SIGALRM into sigset
    if (sigemptyset(&sigset) == -1)
        err_sys("sigemptyet");
    if (sigaddset(&sigset, SIGALRM) == -1)
        err_sys("sigaddset");
    //block SIGALRMsignal
    if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1)
        err_sys("sigprocmask");
    //generate SIGALRM signal
    alarm(1);
    FD_ZERO(&rset);
    FD_SET(STDIN_FILENO, &rset);
    maxfdp1 = STDIN_FILENO + 1;
    if (select(maxfdp1, &rset, NULL, NULL, NULL) <= 0)
        //if (pselect(maxfdp1, &rset, NULL, NULL, NULL, &zeromask)<= 0)
        err_sys("pselect error");
    if (FD_ISSET(STDIN_FILENO, &rset))
    {
        if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) == -1)
            err_sys("read error");
        if (write(STDOUT_FILENO, buf, nread) != nread)
            err_sys("write error");
    }
    exit(0);
}

void sig_int(int signo)
{
    char s[] = "received";
    psignal(signo, s);
    return;
}

void err_sys(const char *p_error)
{
    perror(p_error);
    exit(1);
}

上段代码如果没有 CTRL+C 送上一个 SIGINT 信号,将永远阻塞在与用户的交互上,ALARM 产生的 SIGALRM 信号永远打断不了 PSELECT,ALARM 信号被成功屏蔽

  1. 如果上面代码改成下面则无法屏蔽信号,因为我们使用 zeromask,它不屏蔽任何信号
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#define BUFFSIZE 80
void sig_int(int signo);
void err_sys(const char *p_error);

void sig_alrm(int signo)
{
    char s[] = "receive";
    psignal(signo, s);
    return;
}

int main(int argc, char **argv)
{
    int maxfdp1;
    fd_set rset;
    sigset_t zeromask;
    ssize_t nread;
    char buf[BUFFSIZE];
    sigset_t sigset;
    struct sigaction act;
    //set SIGALRM signal handler
    act.sa_handler = sig_alrm;
    if (sigemptyset(&act.sa_mask) == -1)
        err_sys("sigemptyset");
    if (sigemptyset(&zeromask) == -1)
        err_sys("sigemptyset");
    act.sa_flags = 0;
    if (sigaction(SIGALRM, &act, NULL) == -1)
        err_sys("sigaction");
    //initialize signal set and addition SIGALRM into sigset
    if (sigemptyset(&sigset) == -1)
        err_sys("sigemptyet");
    if (sigaddset(&sigset, SIGALRM) == -1)
        err_sys("sigaddset");
    //block SIGALRMsignal
    if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1)
        err_sys("sigprocmask");
    //generate SIGALRM signal
    alarm(1);
    FD_ZERO(&rset);
    FD_SET(STDIN_FILENO, &rset);
    maxfdp1 = STDIN_FILENO + 1;
    //if (select(maxfdp1, &rset, NULL, NULL, NULL)<= 0)
    if (pselect(maxfdp1, &rset, NULL, NULL, NULL, &zeromask) <= 0)
        err_sys("pselect error");
    if (FD_ISSET(STDIN_FILENO, &rset))
    {
        if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) == -1)
            err_sys("read error");
        if (write(STDOUT_FILENO, buf, nread) != nread)
            err_sys("write error");
    }
    exit(0);
}

void sig_int(int signo)
{
    char s[] = "received";
    psignal(signo, s);
    return;
}

void err_sys(const char *p_error)
{
    perror(p_error);
    exit(1);
}
  1. 仅仅使用 select,不使用 sigprocmask 也无法屏蔽信号
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#define BUFFSIZE 80
void sig_int(int signo);
void err_sys(const char *p_error);

void sig_alrm(int signo)
{
    char s[] = "receive";
    psignal(signo, s);
    return;
}

int main(int argc, char **argv)
{
    int maxfdp1;
    fd_set rset;
    sigset_t sigmask;
    ssize_t nread;
    char buf[BUFFSIZE];
    sigset_t sigset;
    struct sigaction act;
    //set SIGALRM signal handler
    act.sa_handler = sig_alrm;
    if (sigemptyset(&act.sa_mask) == -1)
        err_sys("sigemptyset");
    act.sa_flags = 0;
    if (sigaction(SIGALRM, &act, NULL) == -1)
        err_sys("sigaction");
    //initialize signal set and addition SIGALRM into sigset
    if (sigemptyset(&sigset) == -1)
        err_sys("sigemptyet");
    if (sigaddset(&sigset, SIGALRM) == -1)
        err_sys("sigaddset");
    alarm(1);
    FD_ZERO(&rset);
    FD_SET(STDIN_FILENO, &rset);
    maxfdp1 = STDIN_FILENO + 1;
    //if (pselect(maxfdp1, &rset, NULL, NULL, NULL,&sigset) <= 0)
    if (select(maxfdp1, &rset, NULL, NULL, NULL) <= 0)
        err_sys("pselect error");
    if (FD_ISSET(STDIN_FILENO, &rset))
    {
        if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) == -1)
            err_sys("read error");
        if (write(STDOUT_FILENO, buf, nread) != nread)
            err_sys("write error");
    }
    exit(0);
}

void sig_int(int signo)
{
    char s[] = "received";
    psignal(signo, s);
    return;
}

void err_sys(const char *p_error)
{
    perror(p_error);
    exit(1);
}
  1. 用 pselect,但信号屏蔽参数为 NULL, 同使用 select 一样,无法屏蔽信号
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#define BUFFSIZE 80
void sig_int(int signo);
void err_sys(const char *p_error);

void sig_alrm(int signo)
{
    char s[] = "receive";
    psignal(signo, s);
    return;
}

int main(int argc, char **argv)
{
    int maxfdp1;
    fd_set rset;
    sigset_t sigmask;
    ssize_t nread;
    char buf[BUFFSIZE];
    sigset_t sigset;
    struct sigaction act;
    //set SIGALRM signal handler
    act.sa_handler = sig_alrm;
    if (sigemptyset(&act.sa_mask) == -1)
        err_sys("sigemptyset");
    act.sa_flags = 0;
    if (sigaction(SIGALRM, &act, NULL) == -1)
        err_sys("sigaction");
    //initialize signal set and addition SIGALRM into sigset
    if (sigemptyset(&sigset) == -1)
        err_sys("sigemptyet");
    if (sigaddset(&sigset, SIGALRM) == -1)
        err_sys("sigaddset");
    alarm(1);
    FD_ZERO(&rset);
    FD_SET(STDIN_FILENO, &rset);
    maxfdp1 = STDIN_FILENO + 1;
    //if (pselect(maxfdp1, &rset, NULL, NULL, NULL,&sigset) <= 0)
    if (pselect(maxfdp1, &rset, NULL, NULL, NULL, NULL) <= 0)
        //if (select(maxfdp1, &rset, NULL, NULL, NULL) <= 0)
        err_sys("pselect error");
    if (FD_ISSET(STDIN_FILENO, &rset))
    {
        if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) == -1)
            err_sys("read error");
        if (write(STDOUT_FILENO, buf, nread) != nread)
            err_sys("write error");
    }
    exit(0);
}

void sig_int(int signo)
{
    char s[] = "received";
    psignal(signo, s);
    return;
}

void err_sys(const char *p_error)
{
    perror(p_error);
    exit(1);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值