?保存信號

? 信號其他相關(guān)常?概念實際執(zhí)?信號的處理動作稱為信號遞達(dá)(Delivery)信號從產(chǎn)?到遞達(dá)之間的狀態(tài),稱為信號未決(Pending)。進(jìn)程可以選擇阻塞(Block)某個信號。被阻塞的信號產(chǎn)?時將保持在未決狀態(tài),直到進(jìn)程解除對此信號的阻塞,才執(zhí)?遞達(dá)的動作.注意,阻塞和忽略是不同的,只要信號被阻塞就不會遞達(dá),?忽略是在遞達(dá)之后可選的?種處理動作。?在內(nèi)核中的表?
信號在內(nèi)核中的表??意圖

每個信號都有兩個標(biāo)志位分別表?阻塞(block)和未(pending),還有?個函數(shù)指針表?處理動作。信號產(chǎn)?時,內(nèi)核在進(jìn)程控制塊中設(shè)置該信號的未決標(biāo)志,直到信號遞達(dá)才清除該標(biāo)志。在上圖的例?中,SIGHUP信號未阻塞也未產(chǎn)?過,當(dāng)它遞達(dá)時執(zhí)?默認(rèn)處理動作。SIGINT信號產(chǎn)?過,但正在被阻塞,所以暫時不能遞達(dá)。雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個信號,因為進(jìn)程仍有機(jī)會改變處理動作之后再解除阻塞。SIGQUIT信號未產(chǎn)?過,?旦產(chǎn)?SIGQUIT信號將被阻塞,它的處理動作是???定義函數(shù)sighandler。
如果在進(jìn)程解除對某信號的阻塞之前這種信號產(chǎn)?過多次,將如何處理?POSIX.1允許系統(tǒng)遞送該信 號?次或多次。linux是這樣實現(xiàn)的:常規(guī)信號在遞達(dá)之前產(chǎn)?多次只計?次,?實時信號在遞達(dá)之 前產(chǎn)?多次可以依次放在?個隊列?。本章不討論實時信號。
代碼語言:JavaScript代碼運行次數(shù):0運行復(fù)制
內(nèi)核結(jié)構(gòu)2.6.18 struct task_struct{ ... /* signal handlers */ struct sighand_struct *sighand; sigset_t blocked; struct sigpending pending; ...}struct sighand_struct{ atomic_t count; struct k_sigaction action[_NSIG];// #define _NSIG 64 spinlock_t siglock; }struct __new_sigaction{ __sighandler_t sa_handler; unsigned long sa_flags; void (*sa_restorer)(void); /* Not used by Linux /SPARC */ __new_sigset_t sa_mask;}struct k_sigaction{ struct __new_sigactionsa; void __user *ka_restorer;};/* Type of a signal handler. */typedef void struct (*__sighandler_t)(int);struct sigpending{ struct list_head list; sigset_t signal;};
? sigset_t

從上圖來看,每個信號只有?個bit的未決標(biāo)志,?0即1,不記錄該信號產(chǎn)?了多少次,阻塞標(biāo)志也是這樣表?的。因此,未決和阻塞標(biāo)志可以?相同的數(shù)據(jù)類型sigset_t來存儲,sigset_t稱為信號集, 這個類型可以表?每個信號的“有效”或“?效”狀態(tài),在阻塞信號集中“有效”和“?效”的含義是該信號是否被阻塞,?在未決信號集中“有效”和“?效”的含義是該信號是否處于未決狀態(tài)。
將詳細(xì)介紹信號集的各種操作。阻塞信號集也叫做當(dāng)前進(jìn)程的信號屏蔽字(SignalMask),這?的“屏蔽”,應(yīng)該理解為阻塞?不是忽略。
?信號集操作函數(shù)
sigset_t類型對于每種信號??個bit表?“有效”或“?效”狀態(tài),?于這個類型內(nèi)部如何存儲這些。bit則依賴于系統(tǒng)實現(xiàn),從使?者的?度是不必關(guān)?的,使?者只能調(diào)?以下函數(shù)來操作sigset_t變量,?不應(yīng)該對它的內(nèi)部數(shù)據(jù)做任何解釋,?如?printf直接打印sigset_t變量是沒有意義的。
代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
#include <signal.h> int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigaddset(sigset_t *set, int signo); int sigdelset(sigset_t *set, int signo); int sigismember(const sigset_t *set, int signo);
函數(shù)sigemptyset初始化set所指向的信號集,使其中所有信號的對應(yīng)bit清零,表?該信號集不包含任何有效信號。函數(shù)sigfillset初始化set所指向的信號集,使其中所有信號的對應(yīng)bit置位,表?該信號集的有效信號包括系統(tǒng)?持的所有信號。注意,在使?sigset_t類型的變量之前,?定要調(diào)?sigemptyset或sigfillset做初始化,使信號集處于確定的狀態(tài)。初始化sigset_t變量之后就可以在調(diào)?sigaddset和sigdelset在該信號集中添加或刪除某種有效信號。

?sigprocmask
調(diào)?函數(shù) sigprocmask 可以讀取或更改進(jìn)程的信號屏蔽字(阻塞信號集)。
代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oset);返回值若成功則為0,若出錯則為-1
如果oset是?空指針,則讀取進(jìn)程的當(dāng)前信號屏蔽字通過oset參數(shù)傳出。如果set是?空指針,則更改進(jìn)程的信號屏蔽字,參數(shù)how指?如何更改。如果oset和set都是?空指針,則先將原來的信號屏蔽字備份到oset?,然后根據(jù)set和how參數(shù)更改信號屏蔽字。假設(shè)當(dāng)前的信號屏蔽字為mask,下表說明了how參數(shù)的可選值。

如果調(diào)?sigprocmask解除了對當(dāng)前若?個未決信號的阻塞,則在sigprocmask返回前,?少將其中?個信號遞達(dá)。
?sigpending代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
#include <signal.h> int sigpending(sigset_t *set);讀取當(dāng)前進(jìn)程的未決信號集,通過set參數(shù)傳出。調(diào)?成功則返回0,出錯則返回-1
下??剛學(xué)的?個函數(shù)做個實驗。程序如下:

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
#include <iostream>#include <unistd.h>#include <cstdio>#include <sys/types.h>#include <sys/wait.h>void PrintPending(sigset_t &pending){ std::cout << "curr process[" << getpid() << "]pending: "; for (int signo = 31; signo >= 1; signo--) { if (sigismember(&pending, signo)) { std::cout << 1; } else { std::cout << 0; } } std::cout << "n";}void handler(int signo){ std::cout << signo << " 號信號被遞達(dá) !!!" << std::endl; std::cout << "-------------------------------" << std::endl; sigset_t pending; sigpending(&pending); PrintPending(pending); std::cout << "-------------------------------" << std::endl;}int main(){ // 0.捕捉2號信號 signal(2, handler); // ?定義捕捉 // signal(2, SIG_IGN); //忽略?個信號 // signal(2, SIG_DFL); //信號的默認(rèn)處理動作 // 1.屏蔽 2 號信號 sigset_t block_set, old_set; sigemptyset(&block_set); sigemptyset(&old_set); sigaddset(&block_set, SIGINT); // 我們有沒有修改當(dāng)前進(jìn)?的內(nèi)核block表呢??? 1 0 // 1.1設(shè)置進(jìn)?進(jìn)程的Block表中 sigprocmask(SIG_BLOCK, &block_set, &old_set); // 真正的修改當(dāng)前進(jìn)?的內(nèi)核block表,完成了對 2 號信號的屏蔽! int cnt = 15; int cnt = 15; while (true) { // 2. 獲取當(dāng)前進(jìn)程的pending信號集 sigset_t pending; sigpending(&pending); // 3. 打印pending信號集 PrintPending(pending); cnt--; // 4.解除對2 號信號的屏蔽 if (cnt == 0) { std::cout << "解除對 2 號信號的屏蔽 !!!" << std::endl; sigprocmask(SIG_SETMASK, &old_set, &block_set); } sleep(1); }}


程序運?時,每秒鐘把各信號的未決狀態(tài)打印?遍,由于我們阻塞了SIGINT信號,按Ctrl-C將會使SIGINT 信號處于未決狀態(tài),按Ctrl-仍然可以終?程序,因為SIGQUIT信號沒有阻塞。
?總結(jié)
