天天看點

sigsuspend

    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相當于原子的執行下面三個操作:

     注意上面的三條語句是有bug的,假設三條語句分别用#1#、#2#、#3#表示,這是#1#和#2#兩個系統調用存在時間視窗,在這個時間視窗内發生了信号(非屏蔽信号mask中的),那麼pause()将不會收到這個信号,進而沒有喚醒程序(隻有之後再次收到信号才能喚醒程序),這和程式的原意是相悖的,程式原意是想設定了信号屏蔽字後程序立馬阻塞并等待信号的發生。而由于兩個系統調用間的間隙,如果信号在此間隙内産生那麼pause沒有收到該信号,進而和原意在程序阻塞pause()收到信号後喚醒程序相悖。正因為有了以上原因才産生了原子操作sigsuspend的系統調用。

     驗證sigsuspend執行個體:

程式執行:

in critical region

^Cout of critical region  //在臨界區内按下ctrl+c,此時由于sigprocmask阻塞了SIGINT信号,是以該SIGINT在信号阻塞隊列裡

in sig_int 2   //當程式走出臨界區時,由于sigsuspend臨時将信号屏蔽字設為隻屏蔽SIGUSR1信号,剛才在信号阻塞隊列的SIGINT信号觸發其處理函數SIGINT

after return from sigsuspend  //從sigsuspend傳回後信号屏蔽字重新設為sigsuspend之前的,即sigprocmask設定的屏蔽SIGINT信号的情形

^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C  //此時再pause()等待信号産生,由于信号SIGINT被阻塞了,是以無論怎樣按ctrl+c都無法喚醒程序

   以上程式說明了sigsuspend臨時更改信号屏蔽字的特性,如有必要還需手動恢複sigprocmask設定的信号屏蔽字,如下:

程式執行如下:

^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:

程式輸出:

^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的耗電