在信號處理程序中經常調用longjmp函數以返回到程序的主循環中,而不是從該處理程序返回。
但是,調用longjmp有一個問題。當捕捉到一個信號時,進入信號捕捉函數,此時當前信號被自動地加到進程的信號屏蔽字中。這阻止了后來產生的這種信號中斷該信號處理程序。(僅當從信號捕捉函數返回時再將進程的信號屏蔽字復位為原先值:http://www.cnblogs.com/nufangrensheng/p/3515945.html)如果用longjmp跳出信號處理程序,那么,對此進程的信號屏蔽字會發生什么呢()?(setjmp和longjmp保存和恢復信號屏蔽字,還是不保存和恢復,不同的實現各有不同。)
為了允許兩種形式的行為並存,POSIX.1並沒有說明setjmp和longjmp對信號屏蔽字的作用,而是定義了兩個新函數sigsetjmp和siglongjmp。在信號處理程序中進行非局部轉移時使用這兩個函數。
#include <setjmp.h> int sigsetjmp(sigjmp_buf env, int savemask); 返回值:若直接調用則返回0,若從siglongjmp調用返回則返回非0值 void siglongjmp(sigjmp_buf env, int val);
這兩個函數與setjmp和longjmp之間的唯一區別是sigsetjmp增加了一個參數。如果savemask非0,則sigsetjmp在env中保存進程的當前信號屏蔽字。調用siglongjmp時,如果帶 非0 savemask的sigsetjmp調用已經保存了env,則siglongjmp從其中恢復保存的信號屏蔽字。
實例
程序清單10-14演示了在信號處理程序被調用時,系統所設置的信號屏蔽字如果自動地包括剛被捕捉到的信號。該程序也通過實例說明了如何使用sigsetjmp和siglongjmp函數。
程序清單10-14 信號屏蔽字、sigsetjmp和siglongjmp實例
#include "apue.h" #include <setjmp.h> #include <time.h> static void sig_usr1(int), sig_alrm(int); static sigjmp_buf jmpbuf; static volatile sig_atomic_t canjump; int main(void) { if (signal(SIGUSR1, sig_usr1) == SIG_ERR) err_sys("signal(SIGUSR1) error"); if (signal(SIGALRM, sig_alrm) == SIG_ERR) err_sys("signal(SIGALRM) error"); pr_mask("starting main: "); if (sigsetjmp(jmpbuf, 1)) { pr_mask("ending main: "); exit(0); } canjump = 1; /* now sigsetjmp() is OK */ for(; ;) pause(); } static void sig_usr1(int signo) { time_t starttime; if (canjump == 0) return; /* unexpected signal, ignore */ pr_mask("starting sig_usr1: "); alarm(3); /* SIGALRM in 3 seconds */ starttime = time(NULL); for(; ;) /* busy wait for 5 seconds */ if (time(NULL) > starttime + 5) break; pr_mask("finishing sig_usr1: "); canjump = 0; siglongjmp(jmpbuf, 1); /* jump back to main, don't return */ } static void sig_alrm(int signo) { pr_mask("in sig_alrm: "); }
pr_mask函數參見:http://www.cnblogs.com/nufangrensheng/p/3515257.html中程序清單10-10
此程序演示了另一種技術,只要在信號處理程序中調用siglongjmp,就應使用這種技術:僅在調用sigsetjmp之后才將變量canjump設置為非0值。在信號處理程序中檢測此變量,僅當它為非0值時才調用siglongjmp。這提供了一種保護機制,使得在jmpbuf(跳轉緩沖)尚未由sigsetjmp初始化時,防止調用信號處理程序。在一般的C代碼中(不是信號處理程序),對於longjmp並不需要這種保護措施。但是,因為信號可能在任何時候發生,所以在信號處理程序中,需要這種保護措施。
在程序中使用了數據類型sig_atomic_t,這是由ISO C標准定義的變量類型,在寫這種類型的變量時不會被中斷。這種類型的變量總是包括ISO類型修飾符voaltile。
在RedHat Linux 2.6.18版本中執行程序清單10-14並沒有出現我們所預期的結果:當調用一個信號處理程序時,被捕捉到的信號加到進程的當前信號屏蔽字中。當從信號處理程序返回時,恢復原來的屏蔽字。不知為何???
從上面運行結果可以看出,信號屏蔽字始終都是空。本來應該是在調用一個信號處理程序時,被捕捉到的信號加到進程的當前信號屏蔽字中。為啥沒有加進去呢?還是加進去了沒有更新信號屏蔽字呢?
本篇博文內容摘自《UNIX環境高級編程》(第二版),僅作個人學習記錄所用。關於本書可參考:http://www.apuebook.com/。