pselect 和 select
pselect函数是由POSIX发明的,如今许多Unix变种都支持它。
#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); 返回:就绪描述字的个数,0-超时,-1-出错
pselect相对于通常的select有两个变化:
1、pselect使用timespec结构,而不使用timeval结构。timespec结构是POSIX的又一个发明。
struct timespec{
time_t tv_sec; //seconds
long tv_nsec; //nanoseconds
};
这两个结构的区别在于第二个成员:新结构的该成员tv_nsec指定纳秒数,而旧结构的该成员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。
pselect和select 这两个函数基本上是一致,但是有三个区别:
第一点 select函数用的timeout参数,是一个timeval的结构体(包含秒和微秒),然而pselect用的是一个timespec结构体(包含秒和纳秒)
第二点 select函数可能会为了指示还剩多长时间而更新timeout参数,然而pselect不会改变timeout参数
第三点 select函数没有sigmask参数,当pselect的sigmask参数为null时,两者行为时一致的。有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:sigset_t new, old, zero; 2: 3:sigemptyset(&zero); 4:sigemptyset(&new); 5:sigaddset(&new, SIGINT); 6:sigprocmask(SIG_BLOCK, &new, old);//block SIGINT 7:if (intr_flag) 8: handle_intr();//handle the signal 9:if ((nready = pselet(..., &zero)) < 0){ 10: if (errno = EINTR){ 11: if (intr_flag) 12: handle_intr(); 13: } 14: ... 15:}
如果没有sigprocmask(SIG_BLOCK, &new, old)这行代码阻塞信号,程序执行完7,8行之后,你投递INT,于是INT立即被处理,然后进入9行阻塞。
你本意是发送INT进入handle_intr()处理信号,然后pselect全然不知情的阻塞了。
如果有sigprocmask(SIG_BLOCK, &new, old)这行代码,那么你要么在sigprocmask之前投递了INT,那么INT立即被处理,那么7,8行就可以被正确的处理。要么你在sigprocmask之后投递的INT,那么INT不会被处理,一直到pselect取消了阻塞,INT被处理。这个过程保证了你在处理一个信号结果的同时不会再有该信号被处理。
下面举几个例子来说明select和pselect以及sigprocmask的使用
1 用pselect 屏蔽信号
1 #include <time.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <signal.h> 5 #include <unistd.h> 6 #include <sys/select.h> 7 #define BUFFSIZE 80 8 void sig_int(int signo); 9 void err_sys(const char *p_error); 10 void sig_alrm(int signo) 11 { 12 char s[] ="receive"; 13 psignal(signo, s); 14 return; 15 } 16 int 17 main(int argc, char **argv) 18 { 19 int maxfdp1; 20 fd_set rset; 21 sigset_t sigmask; 22 ssize_t nread; 23 char buf[BUFFSIZE]; 24 sigset_t sigset; 25 struct sigaction act; 26 //set SIGALRM signal handler 27 act.sa_handler= sig_alrm; 28 if(sigemptyset(&act.sa_mask) ==-1) 29 err_sys("sigemptyset"); 30 act.sa_flags= 0; 31 if(sigaction(SIGALRM, &act, NULL) == -1) 32 err_sys("sigaction"); 33 //initialize signal set and addition SIGALRM into sigset 34 if(sigemptyset(&sigset) == -1) 35 err_sys("sigemptyet"); 36 if(sigaddset(&sigset, SIGALRM) == -1) 37 err_sys("sigaddset"); 38 alarm(1); 39 FD_ZERO(&rset); 40 FD_SET(STDIN_FILENO, &rset); 41 maxfdp1 = STDIN_FILENO + 1; 42 if (pselect(maxfdp1, &rset, NULL, NULL, NULL,&sigset) <= 0) 43 //if (select(maxfdp1, &rset, NULL, NULL, NULL) <= 0) 44 err_sys("pselect error"); 45 if (FD_ISSET(STDIN_FILENO, &rset)) 46 { 47 if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) == -1) 48 err_sys("read error"); 49 if (write(STDOUT_FILENO, buf, nread) != nread) 50 err_sys("write error"); 51 } 52 exit(0); 53 } 54 void 55 sig_int(int signo) 56 { 57 char s[] ="received"; 58 psignal(signo, s); 59 return; 60 } 61 void 62 err_sys(const char *p_error) 63 { 64 perror(p_error); 65 exit(1); 66 }
上段代码如果没有CTRL+C送上一个SIGINT信号,将永远阻塞在与用户的交互上,ALARM产生的SIGALRM信号永远打断不了PSELECT,ALARM信号被成功屏蔽
2 用sigprocmask 屏蔽信号。
1 #include <time.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <signal.h> 5 #include <unistd.h> 6 #include <sys/select.h> 7 #define BUFFSIZE 80 8 void sig_int(int signo); 9 void err_sys(const char *p_error); 10 void sig_alrm(int signo) 11 { 12 char s[] ="receive"; 13 psignal(signo, s); 14 return; 15 } 16 int 17 main(int argc, char **argv) 18 { 19 int maxfdp1; 20 fd_set rset; 21 sigset_t zeromask; 22 ssize_t nread; 23 char buf[BUFFSIZE]; 24 sigset_t sigset; 25 struct sigaction act; 26 //set SIGALRM signal handler 27 act.sa_handler= sig_alrm; 28 if(sigemptyset(&act.sa_mask) ==-1) 29 err_sys("sigemptyset"); 30 if(sigemptyset(&zeromask) ==-1) 31 err_sys("sigemptyset"); 32 act.sa_flags= 0; 33 if(sigaction(SIGALRM, &act, NULL) == -1) 34 err_sys("sigaction"); 35 //initialize signal set and addition SIGALRM into sigset 36 if(sigemptyset(&sigset) == -1) 37 err_sys("sigemptyet"); 38 if(sigaddset(&sigset, SIGALRM) == -1) 39 err_sys("sigaddset"); 40 //block SIGALRMsignal 41 if(sigprocmask(SIG_BLOCK, &sigset, NULL) == -1) 42 err_sys("sigprocmask"); 43 //generate SIGALRM signal 44 alarm(1); 45 FD_ZERO(&rset); 46 FD_SET(STDIN_FILENO, &rset); 47 maxfdp1 = STDIN_FILENO + 1; 48 if (select(maxfdp1, &rset, NULL, NULL, NULL)<= 0) 49 //if (pselect(maxfdp1, &rset, NULL, NULL, NULL, &zeromask)<= 0) 50 err_sys("pselect error"); 51 if (FD_ISSET(STDIN_FILENO, &rset)) 52 { 53 if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) == -1) 54 err_sys("read error"); 55 if (write(STDOUT_FILENO, buf, nread) != nread) 56 err_sys("write error"); 57 } 58 exit(0); 59 } 60 void 61 sig_int(int signo) 62 { 63 char s[] ="received"; 64 psignal(signo, s); 65 return; 66 } 67 void 68 err_sys(const char *p_error) 69 { 70 perror(p_error); 71 exit(1); 72 }
上段代码如果没有CTRL+C送上一个SIGINT信号,将永远阻塞在与用户的交互上,ALARM产生的SIGALRM信号永远打断不了PSELECT,ALARM信号被成功屏蔽
3 如果 上述code代码改成下面则无法屏蔽信号,因为我们使用zeromask,它不屏蔽任何信号
1 #include <time.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <signal.h> 5 #include <unistd.h> 6 #include <sys/select.h> 7 #define BUFFSIZE 80 8 void sig_int(int signo); 9 void err_sys(const char *p_error); 10 void sig_alrm(int signo) 11 { 12 char s[] ="receive"; 13 psignal(signo, s); 14 return; 15 } 16 int 17 main(int argc, char **argv) 18 { 19 int maxfdp1; 20 fd_set rset; 21 sigset_t zeromask; 22 ssize_t nread; 23 char buf[BUFFSIZE]; 24 sigset_t sigset; 25 struct sigaction act; 26 //set SIGALRM signal handler 27 act.sa_handler= sig_alrm; 28 if(sigemptyset(&act.sa_mask) ==-1) 29 err_sys("sigemptyset"); 30 if(sigemptyset(&zeromask) ==-1) 31 err_sys("sigemptyset"); 32 act.sa_flags= 0; 33 if(sigaction(SIGALRM, &act, NULL) == -1) 34 err_sys("sigaction"); 35 //initialize signal set and addition SIGALRM into sigset 36 if(sigemptyset(&sigset) == -1) 37 err_sys("sigemptyet"); 38 if(sigaddset(&sigset, SIGALRM) == -1) 39 err_sys("sigaddset"); 40 //block SIGALRMsignal 41 if(sigprocmask(SIG_BLOCK, &sigset, NULL) == -1) 42 err_sys("sigprocmask"); 43 //generate SIGALRM signal 44 alarm(1); 45 FD_ZERO(&rset); 46 FD_SET(STDIN_FILENO, &rset); 47 maxfdp1 = STDIN_FILENO + 1; 48 //if (select(maxfdp1, &rset, NULL, NULL, NULL)<= 0) 49 if (pselect(maxfdp1, &rset, NULL, NULL, NULL, &zeromask)<= 0) 50 err_sys("pselect error"); 51 if (FD_ISSET(STDIN_FILENO, &rset)) 52 { 53 if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) == -1) 54 err_sys("read error"); 55 if (write(STDOUT_FILENO, buf, nread) != nread) 56 err_sys("write error"); 57 } 58 exit(0); 59 } 60 void 61 sig_int(int signo) 62 { 63 char s[] ="received"; 64 psignal(signo, s); 65 return; 66 } 67 void 68 err_sys(const char *p_error) 69 { 70 perror(p_error); 71 exit(1); 72 }
4 仅仅使用select,不使用sigprocmask也无法屏蔽信号。
#include <time.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <sys/select.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); }
5 用pselect,但信号屏蔽参数为NULL, 同使用select一样,无法屏蔽信号
1 #include <time.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <signal.h> 5 #include <unistd.h> 6 #include <sys/select.h> 7 #define BUFFSIZE 80 8 void sig_int(int signo); 9 void err_sys(const char *p_error); 10 void sig_alrm(int signo) 11 { 12 char s[] ="receive"; 13 psignal(signo, s); 14 return; 15 } 16 int 17 main(int argc, char **argv) 18 { 19 int maxfdp1; 20 fd_set rset; 21 sigset_t sigmask; 22 ssize_t nread; 23 char buf[BUFFSIZE]; 24 sigset_t sigset; 25 struct sigaction act; 26 //set SIGALRM signal handler 27 act.sa_handler= sig_alrm; 28 if(sigemptyset(&act.sa_mask) ==-1) 29 err_sys("sigemptyset"); 30 act.sa_flags= 0; 31 if(sigaction(SIGALRM, &act, NULL) == -1) 32 err_sys("sigaction"); 33 //initialize signal set and addition SIGALRM into sigset 34 if(sigemptyset(&sigset) == -1) 35 err_sys("sigemptyet"); 36 if(sigaddset(&sigset, SIGALRM) == -1) 37 err_sys("sigaddset"); 38 alarm(1); 39 FD_ZERO(&rset); 40 FD_SET(STDIN_FILENO, &rset); 41 maxfdp1 = STDIN_FILENO + 1; 42 //if (pselect(maxfdp1, &rset, NULL, NULL, NULL,&sigset) <= 0) 43 if (pselect(maxfdp1, &rset, NULL, NULL, NULL,NULL) <= 0) 44 //if (select(maxfdp1, &rset, NULL, NULL, NULL) <= 0) 45 err_sys("pselect error"); 46 if (FD_ISSET(STDIN_FILENO, &rset)) 47 { 48 if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) == -1) 49 err_sys("read error"); 50 if (write(STDOUT_FILENO, buf, nread) != nread) 51 err_sys("write error"); 52 } 53 exit(0); 54 } 55 void 56 sig_int(int signo) 57 { 58 char s[] ="received"; 59 psignal(signo, s); 60 return; 61 } 62 void 63 err_sys(const char *p_error) 64 { 65 perror(p_error); 66 exit(1); 67 }