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