Linux信号实践(3) --信号内核表示
信號在內核中的表示
? ?執行信號的處理動作稱為信號遞達(Delivery),信號從產生到遞達之間的狀態,稱為信號未決(Pending)。進程可以選擇阻塞(Block)某個信號。被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作。
? ?注意,阻塞和忽略是不同,只要信號被阻塞就不會遞達,而忽略是在遞達之后可選的一種處理動作。信號在內核中的表示可以看作是這樣的:
?
圖-信號的發送過程
?
解釋說明:
? 1)PCB進程控制塊(task_struct)中函數有信號屏蔽狀態字(block)和信號未決狀態字(pending)還有是否忽略標志;
? 2) 信號屏蔽狀態字(block),?1代表阻塞、0代表不阻塞;
? ? ? 信號未決狀態字(pending)的1代表未決,0代表信號可以抵達了;
? 3)向進程發送SIGINT,內核首先判斷信號屏蔽狀態字是否阻塞,若阻塞,信號未決狀態字(pending)相應位制成1;若阻塞解除,信號未決狀態字(pending)相應位制成0;表示信號可以抵達了。
? 4)block狀態字、pending狀態字均64位(bit);
? 5)block狀態字用戶可以讀寫,pending狀態字用戶只能讀;這是信號設計機制。
?
? 思考1:狀態字都64bit,編程時,如何表示狀態字那?
??思考2:block狀態字信息如何獲取或者操作那?哪些api?
??思考3:pending狀態字信息如何獲取或者操作那?哪些api?
????答案見下;
?
信號集操作函數(狀態字表示)?
#include <signal.h> int sigemptyset(sigset_t *set); //把信號集清零;(64bit/8=8字節) int sigfillset(sigset_t *set); //把信號集64bit全部置為1 int sigaddset(sigset_t *set, int signo); //根據signo,把信號集中的對應位置成1 int sigdelset(sigset_t *set, int signo); //根據signo,把信號集中的對應位置成0 int sigismember(const sigset_t *set, int signo); //判斷signo是否在信號集中sigprocmask:讀取/更改信號屏蔽狀態字(Block)
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);返回值:若成功則為0,若出錯則為-1
? 讀取:如果oset是非空指針,則讀取進程的當前信號屏蔽字通過oset參數傳出。
? 更改:如果set是非空指針,則更改進程的信號屏蔽字,參數how指示如何更改。如果oset和set都是非空指針,則先將原來的信號屏蔽字備份到oset里,然后根據set和how參數更改信號屏蔽字。假設當前的信號屏蔽字為mask,下表說明了how參數的可選值。
How:
?
?
sigpending獲取信號未決狀態字(pending)信息?
#include <signal.h> int sigpending(sigset_t *set);DESCRIPTION
?sigpending()??returns?the?set?of?signals?that?are?pending?for?delivery?to?the?calling?thread?
(i.e.,?the?signals?which?have?been?raised?while?blocked).??
The?mask?of?pending?signals?is?returned?in?set.
/** 示例1:添加信號SIGINT到信號屏蔽字 此時再按下Ctrl+C鍵, 進程也接收不到SIGINT信號了 **/ inline void err_exit(std::string message); void sigHandler(int signo); void printSigSet(const sigset_t &sigset);int main() { //雖然此處安裝了信號處理函數, 但是該進程還是接收不到SIGINT信號if (signal(SIGINT, sigHandler) == SIG_ERR)err_exit("signal error");//添加信號屏蔽字: 屏蔽SIGINT信號sigset_t addset;sigemptyset(&addset);sigaddset(&addset, SIGINT);if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)err_exit("sigprocmask error");// 不斷打印當前的信號屏蔽字sigset_t sigset;while (true){sigpending(&sigset);printSigSet(sigset);sleep(1);} }inline void err_exit(std::string message) {perror(message.c_str());exit(EXIT_FAILURE); } void sigHandler(int signo) {cout << "catch a signal, number = " << signo << endl; } void printSigSet(const sigset_t &sigset) {for (unsigned i = 1; i < NSIG; ++i){if (sigismember(&sigset, i))putchar('1');elseputchar('0');}putchar('\n'); }/** 示例2:在示例1的基礎上繼續屏蔽SIGINT信號, 但是如果該進程接收到了SIGQUIT信號, 則將對SIGINT信號的屏蔽解除, 需要 1.在main函數中再安裝一個SIGQUIT捕捉函數 2.對sigHandler函數進行改造 **/ int main() {if (signal(SIGINT, sigHandler) == SIG_ERR)err_exit("signal SIGINT error");if (signal(SIGQUIT, sigHandler) == SIG_ERR)err_exit("signal SIGQUIT error");//添加信號屏蔽字: 屏蔽SIGINT信號sigset_t addset;sigemptyset(&addset);sigaddset(&addset, SIGINT);if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)err_exit("sigprocmask error");// 不斷打印當前的信號屏蔽字sigset_t sigset;while (true){sigpending(&sigset);printSigSet(sigset);sleep(1);} }void sigHandler(int signo) {switch (signo){case SIGINT:cout << "catch a signal SIGINT" << endl;break;case SIGQUIT://如果是SIGQUIT信號, 則將SIGINT信號的屏蔽進行解除sigset_t unblockSet;sigemptyset(&unblockSet);sigaddset(&unblockSet, SIGINT);if (sigprocmask(SIG_UNBLOCK, &unblockSet, NULL) == -1)err_exit("sigprocmask unblock error");elsecout << "sigprocmask success" << endl;break;default:cerr << "unknown signal" << endl;break;} } /**連續的按Ctrl+c鍵盤,雖然發送了多個SIGINT信號,但是因為信號是不穩定的(不支持排隊),所以只保留了一個,如下圖 *///示例: 換成實時信號SIGRTMAX int main() {if (signal(SIGRTMAX, sigHandler) == SIG_ERR)err_exit("signal SIGRTMAX error");if (signal(SIGQUIT, sigHandler) == SIG_ERR)err_exit("signal SIGQUIT error");//添加信號屏蔽字: 屏蔽SIGRTMAX信號sigset_t addset;sigemptyset(&addset);sigaddset(&addset, SIGRTMAX);if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)err_exit("sigprocmask error");// 不斷打印當前的信號屏蔽字sigset_t sigset;while (true){sigpending(&sigset);printSigSet(sigset);sleep(1);} } void sigHandler(int signo) {if (signo == SIGRTMAX)cout << "catch a signal SIGRTMAX" << endl;else if (signo == SIGQUIT){sigset_t unblockSet;sigemptyset(&unblockSet);sigaddset(&unblockSet, SIGRTMAX);if (sigprocmask(SIG_UNBLOCK, &unblockSet, NULL) == -1)err_exit("sigprocmask unblock error");elsecout << "sigprocmask success" << endl;}elsecerr << "unknown signal" << endl; }/**?向該進程連續發送四個SIGRTMAX,?則進程都能夠收到**/
?
?
綜合案例
? ?1)?創建子進程與父進程;
? ?2)?注冊SIGINT非實時信號與SIGRTMIN實時信號,并將這兩種信號添加到進程屏蔽信號組中;
? ?3)?注冊用戶自定義信號;
? ?4)?子進程發送5次非實時信號,發5次實時信號;
? ?5)?然后子進程發送SIGUSR1解除進程對SIGINT,SIGTRMIN信號的阻塞
? ?6)?觀察實時信號與非實時信號的區別
//程序示例 void onSigAction(int signalNumber, siginfo_t *sigInfoStruct, void *) {//獲取接收到的數據int receiveNumber = sigInfoStruct->si_int;//如果收到的是SIGUSR1信號,則解除對SIGINT,SIGRTMIN的屏蔽if (signalNumber == SIGUSR1){sigset_t unblockSet;sigemptyset(&unblockSet);sigaddset(&unblockSet,SIGINT);sigaddset(&unblockSet,SIGRTMIN);;sigprocmask(SIG_UNBLOCK,&unblockSet,NULL);//Value值會是亂碼!//cout << "Receive SIGUSR1, Value = " << receiveNumber << endl;cout << "Unblock, Receive SIGUSR1" << endl;}else if (signalNumber == SIGINT){cout << "Receive SIGINT, Value = " << receiveNumber << endl;}else if (signalNumber == SIGRTMIN){cout << "Receive SIGRTMIN, Value = " << receiveNumber << endl;} }int main() {struct sigaction act;//如果需要使得信號處理程序接收額外數據,//則必須將sa_flags位置為SA_SIGINFOact.sa_flags = SA_SIGINFO;sigemptyset(&act.sa_mask);act.sa_sigaction = onSigAction;//注冊信號處理函數if (sigaction(SIGINT,&act,NULL) < 0)err_exit("sigaction SIGINT");if (sigaction(SIGRTMIN,&act,NULL) < 0)err_exit("sigaction SIGRTMIN");if (sigaction(SIGUSR1,&act,NULL) < 0)err_exit("sigaction SIGUSR1");//將信號SIGINT,SIGRTMIN信號阻塞sigset_t blockSet;sigemptyset(&blockSet);sigaddset(&blockSet,SIGINT);sigaddset(&blockSet,SIGRTMIN);if (sigprocmask(SIG_BLOCK,&blockSet,NULL) < 0)err_exit("sigprocmask error");pid_t pid = fork();if (pid == -1)err_exit("fork error");else if (pid == 0){union sigval Value;Value.sival_int = 200;//給父進程發送五次帶額外數據的非可靠信號(其實最終只接受到了一次)for (int i = 0; i < 5; ++i){++ Value.sival_int;if (sigqueue(getppid(),SIGINT,Value) != 0)err_exit("sigqueue error");}//給父進程發送五次帶額外數據的可靠信號(最終接收到了五次!!!)Value.sival_int = 0;for (int i = 0; i < 5; ++i){++ Value.sival_int;if (sigqueue(getppid(),SIGRTMIN,Value) != 0)err_exit("sigqueue error");}//給父進程發送SIGUSR1信號,解除對SIGINT,SIGRTMIN信號的阻塞kill(getppid(),SIGUSR1);}while (true){pause();} }運行結果:
?
?其實只是收到了一份非可靠信號SIGINT!
轉載于:https://www.cnblogs.com/itrena/p/5926969.html
總結
以上是生活随笔為你收集整理的Linux信号实践(3) --信号内核表示的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 高并发高可靠性系统思考1
- 下一篇: Java设计模式(8)组合模式(Comp
