linux之信号
信號:在生活中,我們遇到過不同種類的信號,比如:(交通信號,乃至某個(gè)人的表情,動(dòng)作等帶給你不同的信號)然而,在我們的linux下,我們最熟悉的就是,當(dāng)遇到一個(gè)死循環(huán)的程序時(shí),我們第一想到的就是按ctrl+c,此時(shí)這個(gè)進(jìn)程立馬終止,這是一種通過鍵盤產(chǎn)生的信號。而說起ctrl+c,就引出了前臺進(jìn)程和后臺進(jìn)程。當(dāng)ctrl+c產(chǎn)生的信號只能發(fā)給前臺進(jìn)程。
用kill -l命令就可以查看信號了;
產(chǎn)生信號的另一種方式是:信號異常觸發(fā)系統(tǒng)使該進(jìn)程終止:
例子:
運(yùn)行結(jié)果:
還有一種方式,通過指令來使該進(jìn)程終止:
然后直接運(yùn)行./test:
還有一種方式,通過alarm使進(jìn)程終止;
說起alarm給大家舉個(gè)例子吧,alarm意思是鬧鐘,在這里也同樣代表著當(dāng)你執(zhí)行某個(gè)進(jìn)程時(shí),突然用alarm定時(shí)的時(shí)間到了,這時(shí)鬧鐘發(fā)揮了作用,使得你的進(jìn)程被迫停止,比如:
這是一個(gè)統(tǒng)計(jì)1秒鐘計(jì)數(shù)的程序,當(dāng)一秒鐘到時(shí),就會被SIGALRM信號終止。
運(yùn)行結(jié)果:
下面我們來說一下處理信號的幾種方式吧!
忽略信號;執(zhí)行默認(rèn);執(zhí)行自定義(信號的捕捉)三種方式。(那么問題來了,什么時(shí)候處理呢?答案是適當(dāng)?shù)臅r(shí)候處理)
執(zhí)行信號的處理動(dòng)作稱為信號遞達(dá),信號從產(chǎn)生到遞達(dá)之間的狀態(tài),稱為信號未決。進(jìn)程可以選擇阻塞某個(gè)信號,被阻塞的信號產(chǎn)生時(shí)將保持在未決狀態(tài),直到進(jìn)程解除對此信號的阻塞,才執(zhí)行遞達(dá)的動(dòng)作。
阻塞和忽略是不同,只要信號被阻塞就不會遞達(dá),而忽略則是在遞達(dá)之后可選的一種處理動(dòng)作。
信號在內(nèi)核中是這樣表示的:
block:代表屏蔽狀態(tài)字(1表示阻塞,0表示不阻塞)
pending:代表未決(1表示未決,0表示可以遞達(dá))
handler:代表信號的處理方式(默認(rèn),忽略,自定義)
每個(gè)信號都有兩個(gè)標(biāo)志位分別表示阻塞(block)和未決(pending),還有一個(gè)函數(shù)指針表示處理動(dòng)作。信號產(chǎn)生時(shí),內(nèi)核在進(jìn)程控制塊中設(shè)置該信號的未決標(biāo)志,直到信號遞達(dá)才清除該標(biāo)志。
上面那張表說明:SIGHUP是沒有阻塞也沒有產(chǎn)生,所以處理動(dòng)作為默認(rèn)處理動(dòng)作
SIGINT的block為1,pending也為1,表示正在阻塞,無法遞達(dá)。它的處理動(dòng)作為忽略,但是在沒有解除阻塞之前不能忽略該信號,很可能在解除阻塞前改變?yōu)槠渌奶幚韯?dòng)作。
對于上述的三張表,操作系統(tǒng)中的每個(gè)進(jìn)程運(yùn)行時(shí)都會存在。
SIGQUIT的block為1,pending為0,說明正在被阻塞,解除阻塞后就可以遞達(dá)。處理動(dòng)作為用戶自定義的處理動(dòng)作。
還有一種現(xiàn)象是,對于解除阻塞之前可能會發(fā)送多次信號,這時(shí)操作系統(tǒng)該作何處理呢。這里主要是分為普通信號和實(shí)時(shí)信號,普通信號出現(xiàn)發(fā)送多次的情況,會當(dāng)做是一次信號進(jìn)行處理。而實(shí)時(shí)信號發(fā)送多次的情況,會將這多個(gè)信號存在一個(gè)隊(duì)列中,分別處理各個(gè)信號。我們一般討論的是普通信號,因此只記錄一次,我們將未決和阻塞狀態(tài)用同一個(gè)數(shù)據(jù)類型來存儲sigset_t,信號集為sigset_t。還需要注意的一點(diǎn)是,阻塞信號集也叫做當(dāng)前進(jìn)程的信號屏蔽字。
說了信號集,我們之前說過這些普通信號是以位圖的形式存放的。每一個(gè)bit位表示一種信號是否存在。
對于信號的操作,我們有一組信號集操作函數(shù),如下:
函數(shù)sigemptyset初始化set所指向的信號集,使其中所有信號的對應(yīng)bit清零,表示該信號集不包含任何有效信號。
函數(shù)sigfillset初始化set所指向的信號集,使其中所有信號的對應(yīng)bit置位,表?示 該信號集的有效信號包括系統(tǒng)支
持的所有信號。
在使?用sigset_t類型的變量之前,?一定要調(diào) ?用sigemptyset或sigfillset做初始化,使信號集處于確定的狀態(tài)。
做完初始化之后調(diào)用sigaddset和sigdelset來添加或者刪除信號。
這四個(gè)函數(shù)成功返回0,失敗返回-1;
sigismember用來表示某種信號是否出現(xiàn)在有效信號集中。出現(xiàn)返回1,不出現(xiàn)返回0.
*還有函數(shù)sigprocmask:(讀取或更改進(jìn)程的信號屏蔽字)
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
成功返回0,出錯(cuò)返回-1
這里的oset為輸出型參數(shù),如果oset為非空,則輸出當(dāng)前進(jìn)程的信號屏蔽字,通過oset輸出。如果set為非空,則更改信號屏蔽字,how指示如何更改。如果set和oset都為非空,則將當(dāng)前進(jìn)程的信號屏蔽字備份在oset中,然后再通過how參數(shù)更改信號屏蔽字。
how主要有三種表示:SIG_BLOCK(包含了我們希望添加的信號屏蔽字),SIG_UNBLOCK(包含了我們希望從信號屏蔽字中阻塞的信號),SIG_SETMASK(設(shè)置了當(dāng)前的信號屏蔽字的值);
*函數(shù)sigpending:(讀取當(dāng)前進(jìn)程的未決信號集)
#include <signal.h>
int sigpending(sigset_t *set);
set為輸出型參數(shù),將信號通過set輸出
?小栗子:
#include<stdio.h> #include<signal.h>void PrintSigset(sigset_t *sig) {int i = 0;for(i = 1; i < 32; i++){if(sigismember(sig,i)){printf("1 ");}else{printf("0 ");}}printf("\n"); }int main() {sigset_t sigset,osigset;sigemptyset(&sigset);sigemptyset(&osigset);sigaddset(&sigset,SIGINT);sigprocmask(SIG_BLOCK,&sigset,&osigset);while(1){sigpending(&sigset);PrintSigset(&sigset);sleep(1);}return 0; } 這個(gè)程序?qū)崿F(xiàn)了將2號信號設(shè)置為阻塞信號。所以一直無法遞達(dá)。
程序運(yùn)行時(shí),每秒鐘把各信號的未決狀態(tài)打印一遍,由于我們阻塞了SIGINT信號,按Ctrl-C將會使SIGINT信號處于未決
狀態(tài),按Ctrl-\和ctrl+z仍然可以終止程序,因?yàn)镾IGQUIT信號沒有阻塞。
運(yùn)行結(jié)果:
還有一個(gè)小栗子,我們將2號信號阻塞之后,5秒后解除阻塞。并打印原來信號的狀態(tài)。
#include<stdio.h> #include<signal.h>void PrintSigset(sigset_t *sig) {int i = 0;for(i = 1; i < 32; i++){if(sigismember(sig,i)){printf("1 ");}else{printf("0 ");}}printf("\n"); }void handler(int sig) {printf("pid: %d sig:%d \n",getpid(),sig); } int main() {sigset_t sigset,osigset;sigemptyset(&sigset);sigemptyset(&osigset);sigaddset(&sigset,SIGINT);sigprocmask(SIG_BLOCK,&sigset,&osigset);int count = 0;signal(2,handler);//signal(2,SIG_DFL);while(1){sigpending(&sigset);PrintSigset(&sigset);sleep(1);if(count++ > 5){sigprocmask(SIG_SETMASK,&osigset,NULL);count = 0;}}return 0; }
運(yùn)行結(jié)果:
總結(jié)
- 上一篇: [数据结构]Map和Set
- 下一篇: [Linux]继续探究mysleep函数