int sigsuspend(const sigset_t *mask);//挂起进程直至信号发生
man手册的解释:
sigsuspend() temporarily replaces the signal mask of the calling process with the mask given by mask and then suspends the process until delivery of a signal whose action is to invoke a signal handler or to terminate a process.特别要注意“temporarily”这个词语,表示临时将信号屏蔽字设为mask,并挂起进程直到有信号产生(非屏蔽信号才能唤醒或终止进程),如果信号处理函数返回,那么siguspend将恢复之前的信号屏蔽字(temporarily)
If the signal terminates the process, then sigsuspend() does not return. If the signal is caught, then sigsuspend() returns after the signal handler returns, and the signal mask is restored to the state before the call to sigsuspend().假设sisuspend阻塞进程时产生了信号A,且A不是mask内的屏蔽信号,那么A的信号处理函数有两种情形,一:直接终止进程,此时进程都不存在了,那么sigsuspend当然无须返回了(不存在进程了sigsuspend也不存在了,函数栈嘛); 二:如果信号A的处理函数返回,那么信号屏蔽字恢复到sigsuspend之前的(sigsuspend调用时将信号屏蔽字设为mask,所以要恢复到sigsuspend调用之前的),然后sigsuspend返回-1并将error置为EINTR.
所以sigsuspend相当于原子的执行下面三个操作:
sigprocmask(SIG_SETMASK, &mask, &prevMask);//讲信号屏蔽字设为mask,并将之前的信号屏蔽字保存在preMask中 #1# pause();//阻塞进程直到有信号产生 #2# sigprocmask(SIG_SETMASK, &prevMask, NULL);//恢复信号屏蔽字为之前的preMask中(#1#之前的信号屏蔽字) #3#注意上面的三条语句是有bug的,假设三条语句分别用#1#、#2#、#3#表示,这是#1#和#2#两个系统调用存在时间窗口,在这个时间窗口内发生了信号(非屏蔽信号mask中的),那么pause()将不会收到这个信号,从而没有唤醒进程(只有之后再次收到信号才能唤醒进程),这和程序的原意是相悖的,程序原意是想设置了信号屏蔽字后进程立马阻塞并等待信号的发生。而由于两个系统调用间的间隙,如果信号在此间隙内产生那么pause没有收到该信号,从而和原意在进程阻塞pause()收到信号后唤醒进程相悖。正因为有了以上原因才产生了原子操作sigsuspend的系统调用。
验证sigsuspend实例:
#include<signal.h> #include<unistd.h> #include<iostream> using namespace std; static void sig_int(int); int main(){ sigset_t newmask,oldmask,waitmask; if(signal(SIGINT,sig_int)==SIG_ERR) cout<<"signal(SIGINT) error"<<endl; sigemptyset(&waitmask); sigaddset(&waitmask,SIGUSR1); sigemptyset(&newmask); sigaddset(&newmask,SIGINT); if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0) // cout<<"SIG_BLOCK error"<<endl; cout<<"in critical region"<<endl; sleep(3); cout<<"out of critical region"<<endl; if(sigsuspend(&waitmask)!=-1) cout<<"sigsuspend error"<<endl; signal(SIGINT,sig_int); cout<<"after return from sigsuspend"<<endl; pause(); cout<<"program exit"<<endl; return 0; } static void sig_int(int signo){ cout<<"\nin sig_int "<<signo<<endl; }程序执行: in critical region
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C //此时再pause()等待信号产生,由于信号SIGINT被阻塞了,因此无论怎样按ctrl+c都无法唤醒进程
以上程序说明了sigsuspend临时更改信号屏蔽字的特性,如有必要还需手动恢复sigprocmask设置的信号屏蔽字,如下:
#include<signal.h> #include<unistd.h> #include<iostream> using namespace std; static void sig_int(int); int main(){ sigset_t newmask,oldmask,waitmask; if(signal(SIGINT,sig_int)==SIG_ERR) cout<<"signal(SIGINT) error"<<endl; sigemptyset(&waitmask); sigaddset(&waitmask,SIGUSR1); sigemptyset(&newmask); sigaddset(&newmask,SIGINT); if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0) //#1# cout<<"SIG_BLOCK error"<<endl; cout<<"in critical region"<<endl; sleep(3); cout<<"out of critical region"<<endl; if(sigsuspend(&waitmask)!=-1) cout<<"sigsuspend error"<<endl; signal(SIGINT,sig_int); cout<<"after return from sigsuspend"<<endl; if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0) //#2# cout<<"SIG_SETMASK error"<<endl; pause(); cout<<"program exit"<<endl; return 0; } static void sig_int(int signo){ cout<<"\nin sig_int "<<signo<<endl; }
程序执行如下:
in critical region
^Cout of critical region //在临界区按下ctrl+c产生SIGINT信号,由于SIGINT被屏蔽所以至于信号阻塞队列中
in sig_int 2 //由于sigsuspend临时只屏蔽了SIGUSR1,所以刚才在信号阻塞队列中的SIGINT触发其处理函数
after return from sigsuspend
^C //由于#2#处用sigprocmask恢复了#1#之前的信号屏蔽字,所以SIGINT不再是屏蔽信号,此时按下ctrl+c将触发SIGINT的处理函数
in sig_int 2
program exit
结合上面的程序,如果将SIGINT通过sigsuspend加入信号屏蔽字,那么即使程序从临界区走出也无法响应SIGINT:
#include<signal.h> #include<unistd.h> #include<iostream> using namespace std; static void sig_int(int); int main(){ sigset_t newmask,oldmask,waitmask; if(signal(SIGINT,sig_int)==SIG_ERR) cout<<"signal(SIGINT) error"<<endl; sigemptyset(&waitmask); sigaddset(&waitmask,SIGUSR1); sigemptyset(&newmask); sigaddset(&newmask,SIGINT); if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0) cout<<"SIG_BLOCK error"<<endl; cout<<"in critical region"<<endl; sleep(3); cout<<"out of critical region"<<endl; sigaddset(&waitmask,SIGINT); if(sigsuspend(&waitmask)!=-1)//此时通过sigsuspend临时将SIGINT和SIGUSR1加入信号屏蔽字 cout<<"sigsuspend error"<<endl; signal(SIGINT,sig_int); cout<<"after return from sigsuspend"<<endl; cout<<"program exit"<<endl; return 0; } static void sig_int(int signo){ cout<<"\nin sig_int "<<signo<<endl; }
in critical region
^Cout of critical region
^C^C^C^C^C^C^C^C //由于sigsuspend临时讲SIGINT和SIGUSR1加入信号屏蔽字,所以无论是之前临界区按下的ctrl+c还是现在按下ctrl+c都无法唤醒进程
在nginx中,master进程通过sigsuspend阻塞直至有非屏蔽信号唤醒master进程,从而使得master进程的主要任务就是监听用户从命令行发送的信号,除此之外就是睡眠,大大减轻了CPU的耗电