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有两个区别:
-
pselect使用timespec结构,而不使用timeval结构。timespec结构是POSIX的又一个发明。
struct timespec { time_t tv_sec; //秒 long tv_nsec; //纳秒 };
这两个结构的区别在于第二个成员:timespec的成员tv_nsec指定纳秒数,而timeval的该成员tv_usec指定微秒数。
-
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);
示例说明
- 用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信号被成功屏蔽。
- 用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信号被成功屏蔽
- 如果上面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);
}
- 仅仅使用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);
}
- 用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);
}