進(jìn)程間通信之:信號(hào)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
信號(hào)是UNIX中所使用的進(jìn)程通信的一種最古老的方法。它是在軟件層次上對(duì)中斷機(jī)制的一種模擬,是一種異步通信方式。信號(hào)可以直接進(jìn)行用戶空間進(jìn)程和內(nèi)核進(jìn)程之間的交互,內(nèi)核進(jìn)程也可以利用它來(lái)通知用戶空間進(jìn)程發(fā)生了哪些系統(tǒng)事件。它可以在任何時(shí)候發(fā)給某一進(jìn)程,而無(wú)需知道該進(jìn)程的狀態(tài)。如果該進(jìn)程當(dāng)前并未處于執(zhí)行態(tài),則該信號(hào)就由內(nèi)核保存起來(lái),直到該進(jìn)程恢復(fù)執(zhí)行再傳遞給它為止;如果一個(gè)信號(hào)被進(jìn)程設(shè)置為阻塞,則該信號(hào)的傳遞被延遲,直到其阻塞被取消時(shí)才被傳遞給進(jìn)程。
在第2章kill命令中曾講解到“−l”選項(xiàng),這個(gè)選項(xiàng)可以列出該系統(tǒng)所支持的所有信號(hào)的列表。在筆者的系統(tǒng)中,信號(hào)值在32之前的則有不同的名稱,而信號(hào)值在32以后的都是用“SIGRTMIN”或“SIGRTMAX”開(kāi)頭的,這就是兩類典型的信號(hào)。前者是從UNIX系統(tǒng)中繼承下來(lái)的信號(hào),為不可靠信號(hào)(也稱為非實(shí)時(shí)信號(hào));后者是為了解決前面“不可靠信號(hào)”的問(wèn)題而進(jìn)行了更改和擴(kuò)充的信號(hào),稱為“可靠信號(hào)”(也稱為實(shí)時(shí)信號(hào))。那么為什么之前的信號(hào)不可靠呢?這里首先要介紹一下信號(hào)的生命周期。
一個(gè)完整的信號(hào)生命周期可以分為3個(gè)重要階段,這3個(gè)階段由4個(gè)重要事件來(lái)刻畫(huà)的:信號(hào)產(chǎn)生、信號(hào)在進(jìn)程中注冊(cè)、信號(hào)在進(jìn)程中注銷、執(zhí)行信號(hào)處理函數(shù),如圖8.6所示。相鄰兩個(gè)事件的時(shí)間間隔構(gòu)成信號(hào)生命周期的一個(gè)階段。要注意這里的信號(hào)處理有多種方式,一般是由內(nèi)核完成的,當(dāng)然也可以由用戶進(jìn)程來(lái)完成,故在此沒(méi)有明確畫(huà)出。
圖8.6信號(hào)生命周期
一個(gè)不可靠信號(hào)的處理過(guò)程是這樣的:如果發(fā)現(xiàn)該信號(hào)已經(jīng)在進(jìn)程中注冊(cè),那么就忽略該信號(hào)。因此,若前一個(gè)信號(hào)還未注銷又產(chǎn)生了相同的信號(hào)就會(huì)產(chǎn)生信號(hào)丟失。而當(dāng)可靠信號(hào)發(fā)送給一個(gè)進(jìn)程時(shí),不管該信號(hào)是否已經(jīng)在進(jìn)程中注冊(cè),都會(huì)被再注冊(cè)一次,因此信號(hào)就不會(huì)丟失。所有可靠信號(hào)都支持排隊(duì),而所有不可靠信號(hào)都不支持排隊(duì)。
注意
這里信號(hào)的產(chǎn)生、注冊(cè)和注銷等是指信號(hào)的內(nèi)部實(shí)現(xiàn)機(jī)制,而不是調(diào)用信號(hào)的函數(shù)實(shí)現(xiàn)。因此,信號(hào)注冊(cè)與否,與本節(jié)后面講到的發(fā)送信號(hào)函數(shù)(如kill()等)以及信號(hào)安裝函數(shù)(如signal()等)無(wú)關(guān),只與信號(hào)值有關(guān)。
用戶進(jìn)程對(duì)信號(hào)的響應(yīng)可以有3種方式。
n 忽略信號(hào),即對(duì)信號(hào)不做任何處理,但是有兩個(gè)信號(hào)不能忽略,即SIGKILL及SIGSTOP。
n 捕捉信號(hào),定義信號(hào)處理函數(shù),當(dāng)信號(hào)發(fā)生時(shí),執(zhí)行相應(yīng)的自定義處理函數(shù)。
n 執(zhí)行缺省操作,Linux對(duì)每種信號(hào)都規(guī)定了默認(rèn)操作。
Linux中的大多數(shù)信號(hào)是提供給內(nèi)核的,表8.6列出了Linux中最為常見(jiàn)信號(hào)的含義及其默認(rèn)操作。
表8.6 常見(jiàn)信號(hào)的含義及其默認(rèn)操作
信號(hào)名
含義
默認(rèn)操作
SIGHUP
該信號(hào)在用戶終端連接(正?;蚍钦#┙Y(jié)束時(shí)發(fā)出,通常是在終端的控制進(jìn)程結(jié)束時(shí),通知同一會(huì)話內(nèi)的各個(gè)作業(yè)與控制終端不再關(guān)聯(lián)
終止
SIGINT
該信號(hào)在用戶鍵入INTR字符(通常是Ctrl-C)時(shí)發(fā)出,終端驅(qū)動(dòng)程序發(fā)送此信號(hào)并送到前臺(tái)進(jìn)程中的每一個(gè)進(jìn)程
終止
SIGQUIT
該信號(hào)和SIGINT類似,但由QUIT字符(通常是Ctrl-)來(lái)控制
終止
SIGILL
該信號(hào)在一個(gè)進(jìn)程企圖執(zhí)行一條非法指令時(shí)(可執(zhí)行文件本身出現(xiàn)錯(cuò)誤,或者試圖執(zhí)行數(shù)據(jù)段、堆棧溢出時(shí))發(fā)出
終止
SIGFPE
該信號(hào)在發(fā)生致命的算術(shù)運(yùn)算錯(cuò)誤時(shí)發(fā)出。這里不僅包括浮點(diǎn)運(yùn)算錯(cuò)誤,還包括溢出及除數(shù)為0等其他所有的算術(shù)錯(cuò)誤
終止
SIGKILL
該信號(hào)用來(lái)立即結(jié)束程序的運(yùn)行,并且不能被阻塞、處理或忽略
終止
SIGALRM
該信號(hào)當(dāng)一個(gè)定時(shí)器到時(shí)的時(shí)候發(fā)出
終止
SIGSTOP
該信號(hào)用于暫停一個(gè)進(jìn)程,且不能被阻塞、處理或忽略
暫停進(jìn)程
SIGTSTP
該信號(hào)用于交互停止進(jìn)程,用戶鍵入SUSP字符時(shí)(通常是Ctrl+Z)發(fā)出這個(gè)信號(hào)
停止進(jìn)程
SIGCHLD
子進(jìn)程改變狀態(tài)時(shí),父進(jìn)程會(huì)收到這個(gè)信號(hào)
忽略
SIGABORT
進(jìn)程異常終止時(shí)發(fā)出
8.3.2信號(hào)發(fā)送與捕捉發(fā)送信號(hào)的函數(shù)主要有kill()、raise()、alarm()以及pause(),下面就依次對(duì)其進(jìn)行介紹。
1.kill()和raise()(1)函數(shù)說(shuō)明。
kill()函數(shù)同讀者熟知的kill系統(tǒng)命令一樣,可以發(fā)送信號(hào)給進(jìn)程或進(jìn)程組(實(shí)際上,kill系統(tǒng)命令只是kill()函數(shù)的一個(gè)用戶接口)。這里需要注意的是,它不僅可以中止進(jìn)程(實(shí)際上發(fā)出SIGKILL信號(hào)),也可以向進(jìn)程發(fā)送其他信號(hào)。
與kill()函數(shù)所不同的是,raise()函數(shù)允許進(jìn)程向自身發(fā)送信號(hào)。
(2)函數(shù)格式。
表8.7列出了kill()函數(shù)的語(yǔ)法要點(diǎn)。
表8.7 kill()函數(shù)語(yǔ)法要點(diǎn)
所需頭文件
#include<signal.h>
#include<sys/types.h>
函數(shù)原型
intkill(pid_tpid,intsig)
函數(shù)傳入值
pid:
正數(shù):要發(fā)送信號(hào)的進(jìn)程號(hào)
0:信號(hào)被發(fā)送到所有和當(dāng)前進(jìn)程在同一個(gè)進(jìn)程組的進(jìn)程
-1:信號(hào)發(fā)給所有的進(jìn)程表中的進(jìn)程(除了進(jìn)程號(hào)最大的進(jìn)程外)
<-1:信號(hào)發(fā)送給進(jìn)程組號(hào)為-pid的每一個(gè)進(jìn)程
sig:信號(hào)
函數(shù)返回值
成功:0
出錯(cuò):-1
表8.8列出了raise()函數(shù)的語(yǔ)法要點(diǎn)。
表8.8 raise()函數(shù)語(yǔ)法要點(diǎn)
所需頭文件
#include<signal.h>
#include<sys/types.h>
函數(shù)原型
intraise(intsig)
函數(shù)傳入值
sig:信號(hào)
函數(shù)返回值
成功:0
出錯(cuò):-1
(3)函數(shù)實(shí)例。
下面這個(gè)示例首先使用fork()創(chuàng)建了一個(gè)子進(jìn)程,接著為了保證子進(jìn)程不在父進(jìn)程調(diào)用kill()之前退出,在子進(jìn)程中使用raise()函數(shù)向自身發(fā)送SIGSTOP信號(hào),使子進(jìn)程暫停。接下來(lái)再在父進(jìn)程中調(diào)用kill()向子進(jìn)程發(fā)送信號(hào),在該示例中使用的是SIGKILL,讀者可以使用其他信號(hào)進(jìn)行練習(xí)。
/*kill_raise.c*/
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h>
intmain()
{
pid_tpid;
intret;
/*創(chuàng)建一子進(jìn)程*/
if((pid=fork())<0)
{
printf("Forkerrorn");
exit(1);
}
if(pid==0)
{
/*在子進(jìn)程中使用raise()函數(shù)發(fā)出SIGSTOP信號(hào),使子進(jìn)程暫停*/
printf("Child(pid:%d)iswaitingforanysignaln",getpid());
raise(SIGSTOP);
exit(0);
}
else
{
/*在父進(jìn)程中收集子進(jìn)程發(fā)出的信號(hào),并調(diào)用kill()函數(shù)進(jìn)行相應(yīng)的操作*/
if((waitpid(pid,NULL,WNOHANG))==0)
{
if((ret=kill(pid,SIGKILL))==0)
{
printf("Parentkill%dn",pid);
}
}
waitpid(pid,NULL,0);
exit(0);
}
}
該程序運(yùn)行結(jié)果如下所示:
$./kill_raise
Child(pid:4877)iswaitingforanysignal
Parentkill4877
2.a(chǎn)larm()和pause()(1)函數(shù)說(shuō)明。
alarm()也稱為鬧鐘函數(shù),它可以在進(jìn)程中設(shè)置一個(gè)定時(shí)器,當(dāng)定時(shí)器指定的時(shí)間到時(shí),它就向進(jìn)程發(fā)送SIGALARM信號(hào)。要注意的是,一個(gè)進(jìn)程只能有一個(gè)鬧鐘時(shí)間,如果在調(diào)用alarm()之前已設(shè)置過(guò)鬧鐘時(shí)間,則任何以前的鬧鐘時(shí)間都被新值所代替。
pause()函數(shù)是用于將調(diào)用進(jìn)程掛起直至捕捉到信號(hào)為止。這個(gè)函數(shù)很常用,通??梢杂糜谂袛嘈盘?hào)是否已到。
(2)函數(shù)格式。
表8.9列出了alarm()函數(shù)的語(yǔ)法要點(diǎn)。
表8.9 alarm()函數(shù)語(yǔ)法要點(diǎn)
所需頭文件
#include<unistd.h>
函數(shù)原型
unsignedintalarm(unsignedintseconds)
函數(shù)傳入值
seconds:指定秒數(shù),系統(tǒng)經(jīng)過(guò)seconds秒之后向該進(jìn)程發(fā)送SIGALRM信號(hào)
函數(shù)返回值
成功:如果調(diào)用此alarm()前,進(jìn)程中已經(jīng)設(shè)置了鬧鐘時(shí)間,則返回上一個(gè)鬧鐘時(shí)間的剩余時(shí)間,否則返回0
出錯(cuò):-1
表8.10列出了pause()函數(shù)的語(yǔ)法要點(diǎn)。
表8.10 pause()函數(shù)語(yǔ)法要點(diǎn)
所需頭文件
#include<unistd.h>
函數(shù)原型
intpause(void)
函數(shù)返回值
-1,并且把error值設(shè)為EINTR
(3)函數(shù)實(shí)例。
該實(shí)例實(shí)際上已完成了一個(gè)簡(jiǎn)單的sleep()函數(shù)的功能,由于SIGALARM默認(rèn)的系統(tǒng)動(dòng)作為終止該進(jìn)程,因此程序在打印信息之前,就會(huì)被結(jié)束了。代碼如下所示:
/*alarm_pause.c*/
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
intmain()
{
/*調(diào)用alarm定時(shí)器函數(shù)*/
intret=alarm(5);
pause();
printf("Ihavebeenwakenup.n",ret);/*此語(yǔ)句不會(huì)被執(zhí)行*/
}
$./alarm_pause
Alarmclock
想一想
用這種形式實(shí)現(xiàn)的sleep()功能有什么問(wèn)題?
8.3.3信號(hào)的處理在了解了信號(hào)的產(chǎn)生與捕獲之后,接下來(lái)就要對(duì)信號(hào)進(jìn)行具體的操作了。從前面的信號(hào)概述中讀者也可以看到,特定的信號(hào)是與一定的進(jìn)程相聯(lián)系的。也就是說(shuō),一個(gè)進(jìn)程可以決定在該進(jìn)程中需要對(duì)哪些信號(hào)進(jìn)行什么樣的處理。例如,一個(gè)進(jìn)程可以選擇忽略某些信號(hào)而只處理其他一些信號(hào),另外,一個(gè)進(jìn)程還可以選擇如何處理信號(hào)??傊?,這些都是與特定的進(jìn)程相聯(lián)系的。因此,首先就要建立進(jìn)程與其信號(hào)之間的對(duì)應(yīng)關(guān)系,這就是信號(hào)的處理。
注意
請(qǐng)讀者注意信號(hào)的注冊(cè)與信號(hào)的處理之間的區(qū)別,前者信號(hào)是主動(dòng)方,而后者進(jìn)程是主動(dòng)方。信號(hào)的注冊(cè)是在進(jìn)程選擇了特定信號(hào)處理之后特定信號(hào)的主動(dòng)行為。
信號(hào)處理的主要方法有兩種,一種是使用簡(jiǎn)單的signal()函數(shù),另一種是使用信號(hào)集函數(shù)組。下面分別介紹這兩種處理方式。
1.信號(hào)處理函數(shù)(1)函數(shù)說(shuō)明。
使用signal()函數(shù)處理時(shí),只需要指出要處理的信號(hào)和處理函數(shù)即可。它主要是用于前32種非實(shí)時(shí)信號(hào)的處理,不支持信號(hào)傳遞信息,但是由于使用簡(jiǎn)單、易于理解,因此也受到很多程序員的歡迎。
Linux還支持一個(gè)更健壯、更新的信號(hào)處理函數(shù)sigaction(),推薦使用該函數(shù)。
(2)函數(shù)格式。
signal()函數(shù)的語(yǔ)法要點(diǎn)如表8.11所示。
表8.11 signal()函數(shù)語(yǔ)法要點(diǎn)
所需頭文件
#include<signal.h>
函數(shù)原型
void(*signal(intsignum,void(*handler)(int)))(int)
函數(shù)傳入值
signum:指定信號(hào)代碼
handler:
SIG_IGN:忽略該信號(hào)
SIG_DFL:采用系統(tǒng)默認(rèn)方式處理信號(hào)
自定義的信號(hào)處理函數(shù)指針
函數(shù)返回值
成功:以前的信號(hào)處理配置
出錯(cuò):-1
這里需要對(duì)這個(gè)函數(shù)原型進(jìn)行說(shuō)明。這個(gè)函數(shù)原型有點(diǎn)復(fù)雜??上扔萌缦碌膖ypedef進(jìn)行替換說(shuō)明:
typedefvoidsign(int);
sign*signal(int,handler*);
可見(jiàn),首先該函數(shù)原型整體指向一個(gè)無(wú)返回值并且?guī)б粋€(gè)整型參數(shù)的函數(shù)指針,也就是信號(hào)的原始配置函數(shù)。接著該原型又帶有兩個(gè)參數(shù),其中的第二個(gè)參數(shù)可以是用戶自定義的信號(hào)處理函數(shù)的函數(shù)指針。
表8.12列舉了sigaction()的語(yǔ)法要點(diǎn)。
表8.12 sigaction()函數(shù)語(yǔ)法要點(diǎn)
所需頭文件
#include<signal.h>
函數(shù)原型
intsigaction(intsignum,conststructsigaction*act,structsigaction*oldact)
函數(shù)傳入值
signum:信號(hào)代碼,可以為除SIGKILL及SIGSTOP外的任何一個(gè)特定有效的信號(hào)
act:指向結(jié)構(gòu)sigaction的一個(gè)實(shí)例的指針,指定對(duì)特定信號(hào)的處理
oldact:保存原來(lái)對(duì)相應(yīng)信號(hào)的處理
函數(shù)返回值
成功:0
出錯(cuò):-1
這里要說(shuō)明的是sigaction()函數(shù)中第2個(gè)和第3個(gè)參數(shù)用到的sigaction結(jié)構(gòu)。這是一個(gè)看似非常復(fù)雜的結(jié)構(gòu),希望讀者能夠慢慢閱讀此段內(nèi)容。
首先給出了sigaction的定義,如下所示:
structsigaction
{
void(*sa_handler)(intsigno);
sigset_tsa_mask;
intsa_flags;
void(*sa_restore)(void);
}
sa_handler是一個(gè)函數(shù)指針,指定信號(hào)處理函數(shù),這里除可以是用戶自定義的處理函數(shù)外,還可以為SIG_DFL(采用缺省的處理方式)或SIG_IGN(忽略信號(hào))。它的處理函數(shù)只有一個(gè)參數(shù),即信號(hào)值。
sa_mask是一個(gè)信號(hào)集,它可以指定在信號(hào)處理程序執(zhí)行過(guò)程中哪些信號(hào)應(yīng)當(dāng)被屏蔽,在調(diào)用信號(hào)捕獲函數(shù)之前,該信號(hào)集要加入到信號(hào)的信號(hào)屏蔽字中。
sa_flags中包含了許多標(biāo)志位,是對(duì)信號(hào)進(jìn)行處理的各個(gè)選擇項(xiàng)。它的常見(jiàn)可選值如表8.13所示。
表8.13 常見(jiàn)信號(hào)的含義及其默認(rèn)操作
選項(xiàng)
含義
SA_NODEFERSA_NOMASK
當(dāng)捕捉到此信號(hào)時(shí),在執(zhí)行其信號(hào)捕捉函數(shù)時(shí),系統(tǒng)不會(huì)自動(dòng)屏蔽此信號(hào)
SA_NOCLDSTOP
進(jìn)程忽略子進(jìn)程產(chǎn)生的任何SIGSTOP、SIGTSTP、SIGTTIN和SIGTTOU信號(hào)
SA_RESTART
令重啟的系統(tǒng)調(diào)用起作用
SA_ONESHOTSA_RESETHAND
自定義信號(hào)只執(zhí)行一次,在執(zhí)行完畢后恢復(fù)信號(hào)的系統(tǒng)默認(rèn)動(dòng)作
(3)使用實(shí)例。
第一個(gè)實(shí)例表明了如何使用signal()函數(shù)捕捉相應(yīng)信號(hào),并做出給定的處理。這里,my_func就是信號(hào)處理的函數(shù)指針。讀者還可以將其改為SIG_IGN或SIG_DFL查看運(yùn)行結(jié)果。第二個(gè)實(shí)例是用sigaction()函數(shù)實(shí)現(xiàn)同樣的功能。
以下是使用signal()函數(shù)的示例:
/*signal.c*/
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
/*自定義信號(hào)處理函數(shù)*/
voidmy_func(intsign_no)
{
if(sign_no==SIGINT)
{
printf("IhavegetSIGINTn");
}
elseif(sign_no==SIGQUIT)
{
printf("IhavegetSIGQUITn");
}
}
intmain()
{
printf("WaitingforsignalSIGINTorSIGQUIT...n");
/*發(fā)出相應(yīng)的信號(hào),并跳轉(zhuǎn)到信號(hào)處理函數(shù)處*/
signal(SIGINT,my_func);
signal(SIGQUIT,my_func);
pause();
exit(0);
}
運(yùn)行結(jié)果如下所示。
$./signal
WaitingforsignalSIGINTorSIGQUIT...
IhavegetSIGINT(按ctrl-c組合鍵)
$./signal
WaitingforsignalSIGINTorSIGQUIT...
IhavegetSIGQUIT(按ctrl-組合鍵)
以下是用sigaction()函數(shù)實(shí)現(xiàn)同樣的功能,下面只列出更新的main()函數(shù)部分。
/*sigaction.c*/
/*前部分省略*/
intmain()
{
structsigactionaction;
printf("WaitingforsignalSIGINTorSIGQUIT...n");
/*sigaction結(jié)構(gòu)初始化*/
action.sa_handler=my_func;
sigemptyset(&action.sa_mask);
action.sa_flags=0;
/*發(fā)出相應(yīng)的信號(hào),并跳轉(zhuǎn)到信號(hào)處理函數(shù)處*/
sigaction(SIGINT,&action,0);
sigaction(SIGQUIT,&action,0);
pause();
exit(0);
}
2.信號(hào)集函數(shù)組(1)函數(shù)說(shuō)明。
使用信號(hào)集函數(shù)組處理信號(hào)時(shí)涉及一系列的函數(shù),這些函數(shù)按照調(diào)用的先后次序可分為以下幾大功能模塊:創(chuàng)建信號(hào)集合、注冊(cè)信號(hào)處理函數(shù)以及檢測(cè)信號(hào)。
其中,創(chuàng)建信號(hào)集合主要用于處理用戶感興趣的一些信號(hào),其函數(shù)包括以下幾個(gè)。
n sigemptyset():將信號(hào)集合初始化為空。
n sigfillset():將信號(hào)集合初始化為包含所有已定義的信號(hào)的集合。
n sigaddset():將指定信號(hào)加入到信號(hào)集合中去。
n sigdelset():將指定信號(hào)從信號(hào)集合中刪除。
n sigismember():查詢指定信號(hào)是否在信號(hào)集合之中。
注冊(cè)信號(hào)處理函數(shù)主要用于決定進(jìn)程如何處理信號(hào)。這里要注意的是,信號(hào)集里的信號(hào)并不是真正可以處理的信號(hào),只有當(dāng)信號(hào)的狀態(tài)處于非阻塞狀態(tài)時(shí)才會(huì)真正起作用。因此,首先使用sigprocmask()函數(shù)檢測(cè)并更改信號(hào)屏蔽字(信號(hào)屏蔽字是用來(lái)指定當(dāng)前被阻塞的一組信號(hào),它們不會(huì)被進(jìn)程接收),然后使用sigaction()函數(shù)來(lái)定義進(jìn)程接收到特定信號(hào)之后的行為。檢測(cè)信號(hào)是信號(hào)處理的后續(xù)步驟,因?yàn)楸蛔枞男盘?hào)不會(huì)傳遞給進(jìn)程,所以這些信號(hào)就處于“未處理”狀態(tài)(也就是進(jìn)程不清楚它的存在)。sigpending()函數(shù)允許進(jìn)程檢測(cè)“未處理”信號(hào),并進(jìn)一步?jīng)Q定對(duì)它們作何處理。
(2)函數(shù)格式。
首先介紹創(chuàng)建信號(hào)集合的函數(shù)格式,表8.14列舉了這一組函數(shù)的語(yǔ)法要點(diǎn)。
表8.14 創(chuàng)建信號(hào)集合函數(shù)語(yǔ)法要點(diǎn)
所需頭文件
#include<signal.h>
函數(shù)原型
intsigemptyset(sigset_t*set)
intsigfillset(sigset_t*set)
intsigaddset(sigset_t*set,intsignum)
intsigdelset(sigset_t*set,intsignum)
intsigismember(sigset_t*set,intsignum)
函數(shù)傳入值
set:信號(hào)集
signum:指定信號(hào)代碼
函數(shù)返回值
成功:0(sigismember成功返回1,失敗返回0)
出錯(cuò):-1
表8.15列舉了sigprocmask的語(yǔ)法要點(diǎn)。
表8.15 sigprocmask函數(shù)語(yǔ)法要點(diǎn)
所需頭文件
#include<signal.h>
函數(shù)原型
intsigprocmask(inthow,constsigset_t*set,sigset_t*oset)
函數(shù)傳入值
how:決定函數(shù)的操作方式
SIG_BLOCK:增加一個(gè)信號(hào)集合到當(dāng)前進(jìn)程的阻塞集合之中
SIG_UNBLOCK:從當(dāng)前的阻塞集合之中刪除一個(gè)信號(hào)集合
SIG_SETMASK:將當(dāng)前的信號(hào)集合設(shè)置為信號(hào)阻塞集合
set:指定信號(hào)集
oset:信號(hào)屏蔽字
函數(shù)返回值
成功:0
出錯(cuò):-1
此處,若set是一個(gè)非空指針,則參數(shù)how表示函數(shù)的操作方式;若how為空,則表示忽略此操作。
最后,表8.16列舉了sigpending函數(shù)的語(yǔ)法要點(diǎn)。
表8.16 sigpending函數(shù)語(yǔ)法要點(diǎn)
所需頭文件
#include<signal.h>
函數(shù)原型
intsigpending(sigset_t*set)
函數(shù)傳入值
set:要檢測(cè)的信號(hào)集
函數(shù)返回值
成功:0
出錯(cuò):-1
總之,在處理信號(hào)時(shí),一般遵循如圖8.7所示的操作流程。
圖8.7一般的信號(hào)操作處理流程
(3)使用實(shí)例。
該實(shí)例首先把SIGQUIT、SIGINT兩個(gè)信號(hào)加入信號(hào)集,然后將該信號(hào)集合設(shè)為阻塞狀態(tài),并進(jìn)入用戶輸入狀態(tài)。用戶只需按任意鍵,就可以立刻將信號(hào)集合設(shè)置為非阻塞狀態(tài),再對(duì)這兩個(gè)信號(hào)分別操作,其中SIGQUIT執(zhí)行默認(rèn)操作,而SIGINT執(zhí)行用戶自定義函數(shù)的操作。源代碼如下所示:
/*sigset.c*/
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
/*自定義的信號(hào)處理函數(shù)*/
voidmy_func(intsignum)
{
printf("Ifyouwanttoquit,pleasetrySIGQUITn");
}
intmain()
{
sigset_tset,pendset;
structsigactionaction1,action2;
/*初始化信號(hào)集為空*/
if(sigemptyset(&set)<0)
{
perror("sigemptyset");
exit(1);
}
/*將相應(yīng)的信號(hào)加入信號(hào)集*/
if(sigaddset(&set,SIGQUIT)<0)
{
perror("sigaddset");
exit(1);
}
if(sigaddset(&set,SIGINT)<0)
{
perror("sigaddset");
exit(1);
}
if(sigismember(&set,SIGINT))
{
sigemptyset(&action1.sa_mask);
action1.sa_handler=my_func;
action1.sa_flags=0;
sigaction(SIGINT,&action1,NULL);
}
if(sigismember(&set,SIGQUIT))
{
sigemptyset(&action2.sa_mask);
action2.sa_handler=SIG_DFL;
action2.sa_flags=0;
sigaction(SIGQUIT,&action2,NULL);
}
/*設(shè)置信號(hào)集屏蔽字,此時(shí)set中的信號(hào)不會(huì)被傳遞給進(jìn)程,暫時(shí)進(jìn)入待處理狀態(tài)*/
if(sigprocmask(SIG_BLOCK,&set,NULL)<0)
{
perror("sigprocmask");
exit(1);
}
else
{
printf("Signalsetwasblocked,Pressanykey!");
getchar();
}
/*在信號(hào)屏蔽字中刪除set中的信號(hào)*/
if(sigprocmask(SIG_UNBLOCK,&set,NULL)<0)
{
perror("sigprocmask");
exit(1);
}
else
{
printf("Signalsetisinunblockstaten");
}
while(1);
exit(0);
}
該程序的運(yùn)行結(jié)果如下所示,可以看見(jiàn),在信號(hào)處于阻塞狀態(tài)時(shí),所發(fā)出的信號(hào)對(duì)進(jìn)程不起作用,并且該信號(hào)進(jìn)入待處理狀態(tài)。讀者輸入任意鍵,并且信號(hào)脫離了阻塞狀態(tài)之后,用戶發(fā)出的信號(hào)才能正常運(yùn)行。這里SIGINT已按照用戶自定義的函數(shù)運(yùn)行,請(qǐng)讀者注意阻塞狀態(tài)下SIGINT的處理和非阻塞狀態(tài)下SIGINT的處理有何不同。
$./sigset
Signalsetwasblocked,Pressanykey!/*此時(shí)按任何鍵可以解除阻塞屏蔽字*/
Ifyouwanttoquit,pleasetrySIGQUIT/*阻塞狀態(tài)下SIGINT的處理*/
Signalsetisinunblockstate/*從信號(hào)屏蔽字中刪除set中的信號(hào)*/
Ifyouwanttoquit,pleasetrySIGQUIT/*非阻塞狀態(tài)下SIGINT的處理*/
Ifyouwanttoquit,pleasetrySIGQUIT
Quit/*非阻塞狀態(tài)下SIGQUIT處理*/





