[英]How does Linux prioritize custom signal handlers?
我們上周進行了一次演講,其中涉及操作系統(在本例中為Linux,在本例中我們的學校服務器使用SUSE Linux 11)如何處理中斷。 需要注意的是,對於大多數信號,您可以捕獲中斷並定義自己的信號處理程序來運行而不是默認值。 我們用一個例子來說明這一點,我發現起初我覺得有趣的行為。 這是代碼:
#include <stdio.h>
#include <signal.h>
#define INPUTLEN 100
main(int ac, char *av[])
{
void inthandler (int);
void quithandler (int);
char input[INPUTLEN];
int nchars;
signal(SIGINT, inthandler);
signal(SIGQUIT, quithandler);
do {
printf("\nType a message\n");
nchars = read(0, input, (INPUTLEN - 1));
if ( nchars == -1)
perror("read returned an error");
else {
input[nchars] = '\0';
printf("You typed: %s", input);
}
}
while(strncmp(input, "quit" , 4) != 0);
}
void inthandler(int s)
{
printf(" Received Signal %d ....waiting\n", s);
int i = 0;
for(int i; i<3; ++i){
sleep(1);
printf("inth=%d\n",i);
}
printf(" Leaving inthandler \n");
}
void quithandler(int s)
{
printf(" Received Signal %d ....waiting\n", s);
for(int i; i<7; ++i){
sleep(1);
printf("quith=%d\n",i);
} printf(" Leaving quithandler \n");
}
所以,在運行這段代碼時,我期待這樣的事情:
我發現基於觀察的東西,似乎是信號的嵌套,2隊列深度“調度”。 例如,如果我快速連續輸入以下中斷:
我將從代碼中收到以下行為/輸出:
^CReceived signal 2 ....waiting
^\Received Signal 3 ....waiting
^C^\^\^C quith=0
quith=1
quith=2
quith=3
quith=4
quith=5
quith=6
quith=7
Leaving quithandler
Received Signal 3 ....waiting
quith=1
quith=2
quith=3
quith=4
quith=5
quith=6
quith=7
Leaving quithandler
inth=0
inth=1
inth=2
inth=3
Leaving inthandler
Received Signal 2 ....waiting
inth=0
inth=1
inth=2
inth=3
Leaving inthandler
換句話說,它似乎是這樣處理的:
我向我的教授展示了這種行為,他似乎同意“嵌套2隊列深度”行為正在發生的事情,但我們並不是100%肯定為什么(他來自硬件背景,並且剛剛開始教授這門課程) )。 我想發布SO以了解是否有人可以闡明Linux處理這些信號的原因和方式,因為我們並不期待某些行為即嵌套。
我認為我寫的測試用例應該足以說明發生了什么,但是這里有一些額外測試用例的截圖:
http://imgur.com/Vya7JeY,fjfmrjd,30YRQfk,uHHXFu5,Pj35NbF
我想將其他測試用例留作鏈接,因為它們是一種大型屏幕截圖。
謝謝!
規則(對於非實時信號,例如您正在使用的SIGQUIT
和SIGINT
)是:
掛起狀態是二進制 - 信號處於掛起或未處理狀態。 如果在屏蔽的情況下多次引發信號,則在取消屏蔽時仍然只會傳遞一次。
那么你的例子中發生的是:
SIGINT
並且inthandler()
信號處理程序開始執行,並且SIGINT
被屏蔽。 SIGQUIT
,並且quithandler()
信號處理程序開始執行(使用SIGQUIT
屏蔽中斷inthandler()
)。 SIGINT
,將SIGINT
添加到待處理信號集(因為它被屏蔽)。 SIGQUIT
,將SIGQUIT
添加到待處理信號集(因為它被屏蔽)。 SIGQUIT
,但沒有任何反應因為SIGQUIT
已經掛起了。 SIGINT
,但沒有任何反應,因為SIGINT
已經掛起。 quithandler()
完成執行, SIGQUIT
被取消屏蔽。 因為SIGQUIT
quithandler()
掛起狀態,所以它會被傳遞,並且quithandler()
再次開始執行(再次屏蔽SIGQUIT
)。 quithandler()
第二次完成執行, SIGQUIT
被取消屏蔽。 SIGQUIT
沒有掛起,所以inthandler()
然后繼續執行( SIGINT
仍然被屏蔽)。 inthandler()
完成執行,並且SIGINT
未被屏蔽。 因為SIGINT
inthandler()
掛起狀態,所以它會被傳遞,並且inthandler()
再次開始執行(再次屏蔽SIGINT
)。 inthandler()
第二次完成執行,並且SIGINT
被取消屏蔽。 然后主要功能繼續執行。 在Linux上,您可以通過檢查/proc/<PID>/status
來查看進程的當前屏蔽和掛起信號集。 掩蔽信號被顯示在SigBlk:
位掩碼,並在未決的信號SigPnd:
位掩碼。
如果使用sigaction()
而不是signal()
安裝信號處理程序,則可以指定SA_NODEFER
標志,以請求在處理程序執行時不屏蔽信號。 您可以在程序中嘗試這個 - 使用一個或兩個信號 - 並嘗試預測輸出的樣子。
我在聯機幫助signal (7)
發現了這一點似乎相關:
實時信號以保證順序傳送。 相同類型的多個實時信號按發送順序傳送。 如果向進程發送不同的實時信號,則從編號最小的信號開始傳送它們。 (即,低編號信號具有最高優先級。)相反, 如果多個標准信號等待進程,則未指定它們的傳遞順序。
除了signal (7)
之外,查看sigprocmask
和sigpending
文檔應該可以增強您對待處理信號保證的理解。
要從弱的“未指定”保證轉移到您的OpenSUSE版本上實際發生的事情,您可能需要檢查內核中的信號傳遞代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.