信號(hào)量一般用于進(jìn)程之間通信,在c++關(guān)于信號(hào)量及其相關(guān)函數(shù)的定義均聲明在signal.h中(linux系統(tǒng))。信號(hào)集是信號(hào)量的集合,可以使用sigset_t進(jìn)行定義,我們一般需要關(guān)注的信號(hào)集為阻塞集與未決集,阻塞集指明將哪些信號(hào)阻塞,集合中對(duì)應(yīng)位置1,表示阻塞該信號(hào)。未決集指明哪些信號(hào)還未處理,對(duì)應(yīng)位為1,指該信號(hào)還未從處理。
sigaction()函數(shù)
該函數(shù)的作用對(duì)象為單個(gè)信號(hào),功能是對(duì)信號(hào)作出相應(yīng)的處理動(dòng)作,函數(shù)原型為
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
- 第一個(gè)參數(shù)是要捕獲的信號(hào),比如
SIGCHLD就是子進(jìn)程死亡之后發(fā)出的信號(hào),填入之后,父進(jìn)程就會(huì)監(jiān)視并捕獲該信號(hào)以作出相應(yīng)的動(dòng)作。 - 第二個(gè)參數(shù)是一個(gè)結(jié)構(gòu)體(下文會(huì)詳細(xì)解釋該結(jié)構(gòu)體),其主要的作用就是指明捕獲到信號(hào)之后要執(zhí)行的動(dòng)作。
- 第三個(gè)參數(shù)是保存與該信號(hào)之前的動(dòng)作,如果不保存可以直接置空。
sigaction的結(jié)構(gòu)體聲明如下:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void*);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
對(duì)于上述的結(jié)構(gòu)體我們只關(guān)注三個(gè)屬性:
-
void (*sa_handler)(int):該函數(shù)指針指向與信號(hào)綁定的動(dòng)作函數(shù),即只要信號(hào)被捕獲,就執(zhí)行該函數(shù)指針指向的函數(shù)。 -
sigset_t sa_mask:設(shè)置阻塞集,表示執(zhí)行信號(hào)動(dòng)作時(shí)需要阻塞哪些信號(hào),在c++里默認(rèn)新到來的信號(hào)會(huì)阻塞之前的信號(hào),也就是說如果進(jìn)程正在執(zhí)行信號(hào)A的處理函數(shù),此時(shí)又捕獲到一個(gè)信號(hào)B,B會(huì)將A阻塞,進(jìn)程會(huì)暫停A的處理函數(shù)轉(zhuǎn)而執(zhí)行B的處理函數(shù),之后再回到斷口處,繼續(xù)執(zhí)行A的處理函數(shù)。如果希望在處理A的處理函數(shù)時(shí)不被打斷,就需要設(shè)置阻塞集,表示阻塞那些函數(shù),如果不需要阻塞,可以使用sigemptyset()將阻塞集置空就好。 -
int sa_flags:特殊標(biāo)志,一般置0,如果置1,會(huì)選擇另一種結(jié)構(gòu)體,不予考慮。
信號(hào)集相關(guān)函數(shù)
上節(jié)描述了如何使用sigaction捕獲信號(hào)并觸發(fā)相應(yīng)的動(dòng)作,但是在一個(gè)進(jìn)程中往往不止有一個(gè)信號(hào),這就涉及信號(hào)的阻塞和排隊(duì)問題,默認(rèn)情況下,新捕獲的信號(hào)會(huì)打斷舊信號(hào)的處理函數(shù)的執(zhí)行,當(dāng)然,signal.h頭文件中定義了一系列關(guān)于信號(hào)集的操作,來幫助我們順暢的管理信號(hào)集。
int sigemptyset(sigset_t *set)
該函數(shù)將傳入的信號(hào)集所有位初始化為0,一般用于信號(hào)集的初始化,不做修改將不阻塞而任何信號(hào)。
int sigfillset(sigset_t *set)
該函數(shù)將傳入的信號(hào)集所有初始化為1,一般用于初始化,不做修改將阻塞所有信號(hào)。
int sigaddset(sigset_t *set, int signum)
該函數(shù)將指定信號(hào)加入信號(hào)集,參數(shù)一為傳入的信號(hào)集,參數(shù)二signum為指定的要加入到set中的信號(hào)。
int sigdelset(sigset_t *set, int signum)
該函數(shù)將指定信號(hào)從信號(hào)集中刪除,即將信號(hào)signum從信號(hào)集set中刪除
int sigismember(const sigset_t *set, int signum)
該函數(shù)判斷傳入的信號(hào)集中上是否包含某特定信號(hào),即判斷信號(hào)集set中是否包含信號(hào)signum
sigprocmask
上一小節(jié)中介紹了信號(hào)集與其基本的操作(包括初始化、插入、刪除等),我們創(chuàng)建信號(hào)集的目的是為了幫助流暢的捕獲信號(hào),所以我們需要把創(chuàng)建好的信號(hào)集作為一種配置來控制信號(hào)的捕獲順序,以免信號(hào)的默認(rèn)中斷機(jī)制打斷我們對(duì)某一信號(hào)的持續(xù)捕獲。signal.h允許我們使用sigprocmask函數(shù)將自定義信號(hào)集作為阻塞的配置。該函數(shù)原型為:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
參數(shù)解析:
-
int how:該參數(shù)表示操作類型,即要對(duì)傳入的信號(hào)集做什么操作,是將信號(hào)集加入阻塞,還是為該信號(hào)集中的信號(hào)清除阻塞狀態(tài),該參數(shù)有三個(gè)值可選:SIG_BLOCK--表示求set與原生的信號(hào)集的并集作為阻塞集,即將set中的信號(hào)加入阻塞;SIG_UNBLOCK--表示求原生信號(hào)集與set的差集作為阻塞集使用,即將set中的信號(hào)清除阻塞狀態(tài)。SIG_SETMASK--將set信號(hào)集將原生的阻塞信號(hào)集覆蓋,直接作為阻塞集使用(用的很少)。 -
const sigset_t *set:傳入的自定義的信號(hào)集 -
sigset_t *oldset:該參數(shù)保存原生的信號(hào)阻塞集,不保存可以置空
代碼演示
#include <unistd.h>
#include <iostream>
#include <signal.h>
#include <wait.h>
void alprint(int num){ //信號(hào)處理函數(shù)
for(int i=0;i<num;++i){
sleep(1);
std::cout<<num<<" "<<i<<std::endl;
}
}
int main(){
sigset_t set;//創(chuàng)建信號(hào)集
sigemptyset(&set);//將信號(hào)集初始化為空
sigaddset(&set,SIGINT);//將鍵盤中斷信號(hào)(ctrl-c)加入信號(hào)集
sigprocmask(SIG_BLOCK,&set,NULL);//加入阻塞集
struct sigaction act; //定義信號(hào)處理結(jié)構(gòu)體
act.sa_handler = alprint; //綁定信號(hào)處理函數(shù)
sigemptyset(&act.sa_mask); //清空阻塞集
act.sa_flags = 0;
int num; //要傳入的信號(hào)
std::cin>>num;
sigaction(num,&act,NULL); //捕獲信號(hào),并執(zhí)行處理程序
raise(num); //發(fā)出信號(hào)
return 0;
}
在上述程序中,我們將鍵盤中斷信號(hào)(ctrl-c)加入阻塞集,然后對(duì)我們鍵入的數(shù)字num進(jìn)行捕捉并打印num次num的值,并且打印期間不會(huì)被ctrl-c打斷。實(shí)驗(yàn)步驟:運(yùn)行上面的程序(linux下),輸入一個(gè)數(shù)字(稍微大點(diǎn),好觀察)然后回車,發(fā)現(xiàn)終端在打印剛剛輸入的數(shù)字,此時(shí)按下ctrl-c,發(fā)現(xiàn)無法打斷程序的執(zhí)行,實(shí)驗(yàn)結(jié)束(可以按ctrl-z來結(jié)束程序)。