APR分析信號(hào)篇

APR分析-信號(hào)篇

U know信號(hào)是Unix的重要系統(tǒng)機(jī)制。信號(hào)機(jī)制使用起來(lái)很簡(jiǎn)單,但是理解起來(lái)有并不是那么Easy。APR

Signal的封裝也并不繁瑣,代碼量很少,所以分析APR Signal的過(guò)程其實(shí)就是學(xué)習(xí)Signal機(jī)制的過(guò)程。

一、信號(hào)介紹

1、Signal“歷史久遠(yuǎn)”,在最初的Unix系統(tǒng)上就能看到它“偉岸”的身影。它的引入用來(lái)進(jìn)行User

Mode進(jìn)程間的交互,系統(tǒng)內(nèi)核也可以利用它通知User Mode進(jìn)程發(fā)生了哪些系統(tǒng)事件。從最開始引入到現(xiàn)在,信號(hào)只是做了很小的一些改動(dòng)(不可靠信號(hào)模型到可靠信號(hào)模型)。

2、信號(hào)服務(wù)于兩個(gè)目的:

1)通知某進(jìn)程某特定事件發(fā)生了;

2)強(qiáng)制其通知進(jìn)程執(zhí)行相應(yīng)的信號(hào)處理程序。

二、基礎(chǔ)概念

1、信號(hào)的一個(gè)特性就是可以在任何時(shí)候發(fā)給某一進(jìn)程,而無(wú)需知道該進(jìn)程的狀態(tài)。如果該進(jìn)程當(dāng)前并未處于執(zhí)行態(tài),則該信號(hào)被內(nèi)核Save起來(lái),直到該進(jìn)程恢復(fù)執(zhí)行才傳遞給它;如果一個(gè)信號(hào)被進(jìn)程設(shè)置為阻塞,則該信號(hào)的傳遞被延遲,直到其阻塞被取消它才被傳遞給進(jìn)程。

2、系統(tǒng)內(nèi)核嚴(yán)格區(qū)分信號(hào)傳送的兩個(gè)階段:

1) Signal Generation :系統(tǒng)內(nèi)核更新目標(biāo)進(jìn)程描述結(jié)構(gòu)來(lái)表示一個(gè)信號(hào)已經(jīng)被發(fā)送出去。

2) Signal Delivery :內(nèi)核強(qiáng)制目標(biāo)進(jìn)程對(duì)信號(hào)做出反應(yīng),或執(zhí)行相關(guān)信號(hào)處理函數(shù),或改變進(jìn)程執(zhí)行狀態(tài)。

信號(hào)的誕生和傳輸我們可以這樣理解:把信號(hào)作為“消費(fèi)品”,其Generation狀態(tài)就是“消費(fèi)品誕生”,其Delivery狀態(tài)就是理解為“被消費(fèi)了”。這樣勢(shì)必存在這樣的一個(gè)情況:“消費(fèi)品誕生了,但是還沒(méi)有被消費(fèi)掉”,在信號(hào)模型中,這樣的狀態(tài)被稱為“pending”(懸而未決)。

任何時(shí)候一個(gè)進(jìn)程只能有一個(gè)這樣的某類型的pending信號(hào),同一進(jìn)程的其他同類型的pending信號(hào)將不排隊(duì),將被簡(jiǎn)單的discard(丟棄)掉。

3、如何消費(fèi)一個(gè)signal

1)忽略該信號(hào);[注1]

2)響應(yīng)該信號(hào),執(zhí)行一特定的信號(hào)處理函數(shù);

3)響應(yīng)該信號(hào),執(zhí)行系統(tǒng)默認(rèn)的處理函數(shù)。包括:Terminate、Dump、Ignore、Stop、Continue等。

這里有特殊:SIGKILL和SIGSTOP兩個(gè)信號(hào)不能忽略、不能捕捉、不能阻塞,而只是執(zhí)行系統(tǒng)默認(rèn)處理函數(shù)。

三、APR Signal封裝

APR Signal源代碼的位置在$(APR_HOME)//threadproc目錄下,本篇blog著重分析unix子目錄下的signals.c文件內(nèi)容,其相應(yīng)頭文件為$(APR_HOME)/include/apr_signal.h。

1、apr_signal函數(shù)

Unix信號(hào)機(jī)制提供的最簡(jiǎn)單最常見的接口是signal函數(shù),用來(lái)設(shè)置某特定信號(hào)的處理函數(shù)。但是由于早期版本和后期版本處理信號(hào)方式的不同,導(dǎo)致現(xiàn)在直接使用signal函數(shù)在不同的平臺(tái)上可能得到不同的結(jié)果。

早期版本處理方式:進(jìn)程每次處理信號(hào)后,隨即將信號(hào)的處理動(dòng)作重置為默認(rèn)值。

后期版本處理方式:進(jìn)程每次處理信號(hào)后,信號(hào)的處理動(dòng)作不被重置為默認(rèn)值。

我們舉例測(cè)試一下:分別在Solaris9、Cygwin和RedHat Linux 9上。

例子:

E.G 1:

void siguser1_handler(int sig);

int main(void)

{

if (signal(SIGUSR1,siguser1_handler) == SIG_ERR) {

perror("siguser1_handler error");

exit(1);

}

while (1) {

pause();

}

}

void siguser1_handler(int sig)

{

printf("insiguser1_handler, %d/n", sig);

}

input:

kill -USR1 9122

kill -USR1 9122

output:(Solaris 9)

in siguser1_handler, 16

用戶信號(hào)1 (程序終止)

output:(Cygwin and RH9)

in siguser1_handler, 30

in siguser1_handler, 30

...

..

E.G 1結(jié)果表示在Solaris 9上,信號(hào)的處理仍然按照早期版本的方式,而Cygwin和RH9則都按照后期版本的方式。

那么有什么替代signal函數(shù)的辦法么?在最新的X/Open和UNIXspecifications中都推薦使用一個(gè)新的信號(hào)接口sigaction,該接口采用后期版本的信號(hào)處理方式。在《Unix高級(jí)環(huán)境編程》中就有使用sigaction實(shí)現(xiàn)signal的方法,而APR恰恰也是使用了該方法實(shí)現(xiàn)了apr_signal。其代碼如下:

APR_DECLARE(apr_sigfunc_t *) apr_signal(int signo, apr_sigfunc_t *func)

{

struct sigaction act, oact;

act.sa_handler = func;

sigemptyset(&act.sa_mask);?------------------(1)

act.sa_flags = 0;

#ifdefSA_INTERRUPT????????????/* SunOS */

act.sa_flags |= SA_INTERRUPT;

#endif

... ...

if (sigaction(signo, &act, &oact) <0)

return SIG_ERR;

return oact.sa_handler;

}

(1)這里有一個(gè)Signal Set(信號(hào)集)的概念,通過(guò)相關(guān)函數(shù)操作信號(hào)集以改變內(nèi)核傳遞信號(hào)給進(jìn)程時(shí)的行為。Unix用sigset_t結(jié)構(gòu)來(lái)表示信號(hào)集。信號(hào)集總是和sigprocmask或sigaction一起使用。關(guān)于信號(hào)集和sigprocmask函數(shù)將在下面詳述。

2、apr_signal_block和apr_signal_unblock

這兩個(gè)函數(shù)分別負(fù)責(zé)阻塞和取消阻塞內(nèi)核傳遞某信號(hào)給目標(biāo)進(jìn)程。其主要利用的就是sigprocmask函數(shù)來(lái)實(shí)現(xiàn)的。每個(gè)進(jìn)程都有其對(duì)應(yīng)的信號(hào)屏蔽字,它讓目標(biāo)進(jìn)程能夠通知內(nèi)核“哪些傳給我的信號(hào)該阻塞,哪些暢通無(wú)阻”。在《Unix高級(jí)環(huán)境編程》中作者有這么一段說(shuō)明“如果在調(diào)用sigprocmask后有任何未決的、不再阻塞的信號(hào),則在sigprocmask返回前,至少將其中之一遞送給該進(jìn)程。”能理解這句我想信號(hào)屏蔽字這塊兒也就沒(méi)什么問(wèn)題了。在Unix高級(jí)環(huán)境編程》中作者舉了一個(gè)很不錯(cuò)的例子,講解的也很詳細(xì)。這里想舉例說(shuō)明的是:如果多次調(diào)用SET_BLOCK的sigprocmask設(shè)置屏蔽字,結(jié)果是什么呢?

E.G 3

int main(void)

{

sigset_t newmask,oldmask, pendmask;

/*設(shè)置進(jìn)程信號(hào)屏蔽字,阻塞SIGQUIT */

sigemptyset(&newmask);

sigaddset(&newmask,SIGQUIT);

if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {

perror("SIG_BLOCK error");

}

printf("1st towait 30 seconds/n");

sleep(30);

/*第一次察看當(dāng)前的處于pend狀態(tài)的信號(hào)*/

if(sigpending(&pendmask) < 0) {

perror("sigpending error");

}

if(sigismember(&pendmask, SIGQUIT)) {

printf("SIGQUIT pending/n");

} else {

printf("SIGQUIT unpending/n");

}

if(sigismember(&pendmask, SIGUSR1)) {

if(sigismember(&pendmask, SIGUSR1)) {

printf("SIGUSR1 pending/n");

} else {

printf("SIGUSR1 unpending/n");

}

/*重新設(shè)置屏蔽字,阻塞SIGUSR1 */

sigemptyset(&newmask);

sigaddset(&newmask,SIGUSR1);

if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {

perror("SIG_BLOCK error");

}

printf("2nd towait 30 seconds/n");

sleep(30);

/*再次察看當(dāng)前的處于pend狀態(tài)的信號(hào)*/

if(sigpending(&pendmask) < 0) {

perror("sigpending error");

}

if(sigismember(&pendmask, SIGQUIT)) {

printf("SIGQUIT pending/n");

} else {

printf("SIGQUIT unpending/n");

}

if(sigismember(&pendmask, SIGUSR1)) {

printf("SIGUSR1 pending/n");

} else {

printf("SIGUSR1 unpending/n");

}

exit(0);

}

//output:

1st to wait 30 seconds

^/

SIGQUIT pending

SIGUSR1 unpending

2nd to wait 30 seconds --這之后發(fā)送kill -USR128821

SIGQUIT pending

SIGUSR1 pending

第一次輸出SIGUSR1unpending是因?yàn)椴⑽窗l(fā)送USR1信號(hào),所以自然為unpending狀態(tài);我想說(shuō)的是第二次重新sigprocmask時(shí)我們僅加入了SIGUSR1,并未顯示加入SIGQUIT,之后察看pending信號(hào)中SIGQUIT仍然為pending狀態(tài),這說(shuō)明兩次SET_BLOCK的sigprocmask調(diào)用是"或"的關(guān)系,第二次SET_BLOCK的sigprocmask調(diào)用不會(huì)將第一次SET_BLOCK的sigprocmask調(diào)用設(shè)置的阻塞信號(hào)變?yōu)榉亲枞摹?/p>

四、總結(jié)

信號(hào)簡(jiǎn)單而強(qiáng)大,如果想深入了解signal的實(shí)現(xiàn),參考資料中的第二本書會(huì)給你滿意的答案。

五、參考資料:

1、《Unix高級(jí)環(huán)境編程》

2、《深入理解Linux內(nèi)核》

[注1]

忽略信號(hào)和阻塞信號(hào)

前者相當(dāng)于一個(gè)消費(fèi)行為,該信號(hào)的狀態(tài)為“已消費(fèi)”,而后者只是將信號(hào)做緩存,等待阻塞打開,再交給進(jìn)程消費(fèi),其狀態(tài)為“未消費(fèi)”,也相當(dāng)于處于pending狀態(tài)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容