pselect 函数

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. 如果上面code代码改成下面则无法屏蔽信号,因为我们使用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);
}
上一篇:(三)WebGIS前端地图显示之根据地理范围换算出瓦片行列号的原理(核心)


下一篇:封装,调用函数,以及参数化