android apr分析,APR分析信号篇
APR分析-信號篇
U know信號是Unix的重要系統(tǒng)機(jī)制。信號機(jī)制使用起來很簡單,但是理解起來有并不是那么Easy。APR
Signal的封裝也并不繁瑣,代碼量很少,所以分析APR Signal的過程其實就是學(xué)習(xí)Signal機(jī)制的過程。
一、信號介紹
1、Signal“歷史久遠(yuǎn)”,在最初的Unix系統(tǒng)上就能看到它“偉岸”的身影。它的引入用來進(jìn)行User
Mode進(jìn)程間的交互,系統(tǒng)內(nèi)核也可以利用它通知User Mode進(jìn)程發(fā)生了哪些系統(tǒng)事件。從最開始引入到現(xiàn)在,信號只是做了很小的一些改動(不可靠信號模型到可靠信號模型)。
2、信號服務(wù)于兩個目的:
1)通知某進(jìn)程某特定事件發(fā)生了;
2)強(qiáng)制其通知進(jìn)程執(zhí)行相應(yīng)的信號處理程序。
二、基礎(chǔ)概念
1、信號的一個特性就是可以在任何時候發(fā)給某一進(jìn)程,而無需知道該進(jìn)程的狀態(tài)。如果該進(jìn)程當(dāng)前并未處于執(zhí)行態(tài),則該信號被內(nèi)核Save起來,直到該進(jìn)程恢復(fù)執(zhí)行才傳遞給它;如果一個信號被進(jìn)程設(shè)置為阻塞,則該信號的傳遞被延遲,直到其阻塞被取消它才被傳遞給進(jìn)程。
2、系統(tǒng)內(nèi)核嚴(yán)格區(qū)分信號傳送的兩個階段:
1) Signal Generation :系統(tǒng)內(nèi)核更新目標(biāo)進(jìn)程描述結(jié)構(gòu)來表示一個信號已經(jīng)被發(fā)送出去。
2) Signal Delivery :內(nèi)核強(qiáng)制目標(biāo)進(jìn)程對信號做出反應(yīng),或執(zhí)行相關(guān)信號處理函數(shù),或改變進(jìn)程執(zhí)行狀態(tài)。
信號的誕生和傳輸我們可以這樣理解:把信號作為“消費品”,其Generation狀態(tài)就是“消費品誕生”,其Delivery狀態(tài)就是理解為“被消費了”。這樣勢必存在這樣的一個情況:“消費品誕生了,但是還沒有被消費掉”,在信號模型中,這樣的狀態(tài)被稱為“pending”(懸而未決)。
任何時候一個進(jìn)程只能有一個這樣的某類型的pending信號,同一進(jìn)程的其他同類型的pending信號將不排隊,將被簡單的discard(丟棄)掉。
3、如何消費一個signal
1)忽略該信號;[注1]
2)響應(yīng)該信號,執(zhí)行一特定的信號處理函數(shù);
3)響應(yīng)該信號,執(zhí)行系統(tǒng)默認(rèn)的處理函數(shù)。包括:Terminate、Dump、Ignore、Stop、Continue等。
這里有特殊:SIGKILL和SIGSTOP兩個信號不能忽略、不能捕捉、不能阻塞,而只是執(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信號機(jī)制提供的最簡單最常見的接口是signal函數(shù),用來設(shè)置某特定信號的處理函數(shù)。但是由于早期版本和后期版本處理信號方式的不同,導(dǎo)致現(xiàn)在直接使用signal函數(shù)在不同的平臺上可能得到不同的結(jié)果。
早期版本處理方式:進(jìn)程每次處理信號后,隨即將信號的處理動作重置為默認(rèn)值。
后期版本處理方式:進(jìn)程每次處理信號后,信號的處理動作不被重置為默認(rèn)值。
我們舉例測試一下:分別在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
用戶信號1 (程序終止)
output:(Cygwin and RH9)
in siguser1_handler, 30
in siguser1_handler, 30
...
..
E.G 1結(jié)果表示在Solaris 9上,信號的處理仍然按照早期版本的方式,而Cygwin和RH9則都按照后期版本的方式。
那么有什么替代signal函數(shù)的辦法么?在最新的X/Open和UNIXspecifications中都推薦使用一個新的信號接口sigaction,該接口采用后期版本的信號處理方式。在《Unix高級環(huán)境編程》中就有使用sigaction實現(xiàn)signal的方法,而APR恰恰也是使用了該方法實現(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)這里有一個Signal Set(信號集)的概念,通過相關(guān)函數(shù)操作信號集以改變內(nèi)核傳遞信號給進(jìn)程時的行為。Unix用sigset_t結(jié)構(gòu)來表示信號集。信號集總是和sigprocmask或sigaction一起使用。關(guān)于信號集和sigprocmask函數(shù)將在下面詳述。
2、apr_signal_block和apr_signal_unblock
這兩個函數(shù)分別負(fù)責(zé)阻塞和取消阻塞內(nèi)核傳遞某信號給目標(biāo)進(jìn)程。其主要利用的就是sigprocmask函數(shù)來實現(xiàn)的。每個進(jìn)程都有其對應(yīng)的信號屏蔽字,它讓目標(biāo)進(jìn)程能夠通知內(nèi)核“哪些傳給我的信號該阻塞,哪些暢通無阻”。在《Unix高級環(huán)境編程》中作者有這么一段說明“如果在調(diào)用sigprocmask后有任何未決的、不再阻塞的信號,則在sigprocmask返回前,至少將其中之一遞送給該進(jìn)程。”能理解這句我想信號屏蔽字這塊兒也就沒什么問題了。在Unix高級環(huán)境編程》中作者舉了一個很不錯的例子,講解的也很詳細(xì)。這里想舉例說明的是:如果多次調(diào)用SET_BLOCK的sigprocmask設(shè)置屏蔽字,結(jié)果是什么呢?
E.G 3
int main(void)
{
sigset_t newmask,oldmask, pendmask;
/*設(shè)置進(jìn)程信號屏蔽字,阻塞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)的信號*/
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)的信號*/
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是因為并未發(fā)送USR1信號,所以自然為unpending狀態(tài);我想說的是第二次重新sigprocmask時我們僅加入了SIGUSR1,并未顯示加入SIGQUIT,之后察看pending信號中SIGQUIT仍然為pending狀態(tài),這說明兩次SET_BLOCK的sigprocmask調(diào)用是"或"的關(guān)系,第二次SET_BLOCK的sigprocmask調(diào)用不會將第一次SET_BLOCK的sigprocmask調(diào)用設(shè)置的阻塞信號變?yōu)榉亲枞摹?/p>
四、總結(jié)
信號簡單而強(qiáng)大,如果想深入了解signal的實現(xiàn),參考資料中的第二本書會給你滿意的答案。
五、參考資料:
1、《Unix高級環(huán)境編程》
2、《深入理解Linux內(nèi)核》
[注1]
忽略信號和阻塞信號
前者相當(dāng)于一個消費行為,該信號的狀態(tài)為“已消費”,而后者只是將信號做緩存,等待阻塞打開,再交給進(jìn)程消費,其狀態(tài)為“未消費”,也相當(dāng)于處于pending狀態(tài)。
總結(jié)
以上是生活随笔為你收集整理的android apr分析,APR分析信号篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 抖音电商或落地至上海 以接近电商零售资源
- 下一篇: 没工作没流水怎么贷款 具有这样的条件也