簡體   English   中英

Linux如何優先考慮自定義信號處理程序?

[英]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");
}

所以,在運行這段代碼時,我期待這樣的事情:

  1. 運行代碼.... ^ C.
  2. 輸入inthandler,執行循環,點擊^ \\
  3. 退出inthandler,進入quithandler,執行quithandler循環
  4. ^ C回到inthandler。 如果我在inthandler中再次執行^ C,則忽略連續的inthandler信號,直到當前的inhanndler完成處理。

我發現基於觀察的東西,似乎是信號的嵌套,2隊列深度“調度”。 例如,如果我快速連續輸入以下中斷:

  • ^ C,^ \\,^ C,^ \\,^ \\,^ C.

我將從代碼中收到以下行為/輸出:

^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

換句話說,它似乎是這樣處理的:

  1. 接收第一個^ C信號
  2. 接收^ \\信號,“延遲”inthandler並進入quithandler
  3. 接收下一個^ C信號,但因為我們已經在inthandler中“嵌套”了,所以把它放在inthandler“隊列”的后面
  4. 接收quithandler,放在quithandler隊列的后面。
  5. 執行quithandler直到queue為空。 忽略第三個quithandler,因為它似乎只有2的隊列深度。
  6. 離開quithandler,然后執行剩余的2個inthandler。 忽略最終的inhanndler,因為隊列深度為2。

我向我的教授展示了這種行為,他似乎同意“嵌套2隊列深度”行為正在發生的事情,但我們並不是100%肯定為什么(他來自硬件背景,並且剛剛開始教授這門課程) )。 我想發布SO以了解是否有人可以闡明Linux處理這些信號的原因和方式,因為我們並不期待某些行為即嵌套。

我認為我寫的測試用例應該足以說明發生了什么,但是這里有一些額外測試用例的截圖:

http://imgur.com/Vya7JeY,fjfmrjd,30YRQfk,uHHXFu5,Pj35NbF

我想將其他測試用例留作鏈接,因為它們是一種大型屏幕截圖。

謝謝!

規則(對於非實時信號,例如您正在使用的SIGQUITSIGINT )是:

  1. 默認情況下,在輸入處理程序時屏蔽信號,在處理程序退出時屏蔽信號;
  2. 如果信號在被屏蔽時被引發,則它將保持掛起狀態 ,並且如果/當該信號未被屏蔽時將被傳送。

掛起狀態是二進制 - 信號處於掛起或未處理狀態。 如果在屏蔽的情況下多次引發信號,則在取消屏蔽時仍然只會傳遞一次。

那么你的例子中發生的是:

  1. 引發SIGINT並且inthandler()信號處理程序開始執行,並且SIGINT被屏蔽。
  2. 引發SIGQUIT ,並且quithandler()信號處理程序開始執行(使用SIGQUIT屏蔽中斷inthandler() )。
  3. 引發SIGINT ,將SIGINT添加到待處理信號集(因為它被屏蔽)。
  4. 引發SIGQUIT ,將SIGQUIT添加到待處理信號集(因為它被屏蔽)。
  5. 引發了SIGQUIT ,但沒有任何反應因為SIGQUIT已經掛起了。
  6. 引發了SIGINT ,但沒有任何反應,因為SIGINT已經掛起。
  7. quithandler()完成執行, SIGQUIT被取消屏蔽。 因為SIGQUIT quithandler()掛起狀態,所以它會被傳遞,並且quithandler()再次開始執行(再次屏蔽SIGQUIT )。
  8. quithandler()第二次完成執行, SIGQUIT被取消屏蔽。 SIGQUIT沒有掛起,所以inthandler()然后繼續執行( SIGINT仍然被屏蔽)。
  9. inthandler()完成執行,並且SIGINT未被屏蔽。 因為SIGINT inthandler()掛起狀態,所以它會被傳遞,並且inthandler()再次開始執行(再次屏蔽SIGINT )。
  10. inthandler()第二次完成執行,並且SIGINT被取消屏蔽。 然后主要功能繼續執行。

在Linux上,您可以通過檢查/proc/<PID>/status來查看進程的當前屏蔽和掛起信號集。 掩蔽信號被顯示在SigBlk:位掩碼,並在未決的信號SigPnd:位掩碼。

如果使用sigaction()而不是signal()安裝信號處理程序,則可以指定SA_NODEFER標志,以請求在處理程序執行時不屏蔽信號。 您可以在程序中嘗試這個 - 使用一個或兩個信號 - 並嘗試預測輸出的樣子。

我在聯機幫助signal (7)發現了這一點似乎相關:

實時信號以保證順序傳送。 相同類型的多個實時信號按發送順序傳送。 如果向進程發送不同的實時信號,則從編號最小的信號開始傳送它們。 (即,低編號信號具有最高優先級。)相反, 如果多個標准信號等待進程,則未指定它們的傳遞順序。

除了signal (7)之外,查看sigprocmasksigpending文檔應該可以增強您對待處理信號保證的理解。

要從弱的“未指定”保證轉移到您的OpenSUSE版本上實際發生的事情,您可能需要檢查內核中的信號傳遞代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM