Linux訊號機制與訊號處理

pythontab發表於2018-01-05

定義

訊號(signal)是Linux程式間通訊的一種機制,全稱為軟中斷訊號,也被稱為軟中斷。訊號本質上是在軟體層次上對硬體中斷機制的一種模擬。

與其他程式間通訊方式(例如管道、共享記憶體等)相比,訊號所能傳遞的資訊比較少,只是一個整數。訊號只是用來通知某程式發生了什麼事件,並不給該程式傳遞任何資料。但正是由於傳遞的資訊量少,訊號也便於管理和使用,可以用於系統管理相關的任務,例如通知程式終結、中止或者恢復等。

每種訊號用一個整型常量宏表示,以SIG開頭,比如SIGCHLD、SIGINT等,它們在系統標頭檔案中定義。

訊號由核心(kernel)管理,產生方式多種多樣:

可以由核心自身產生,比如出現硬體錯誤、記憶體讀取錯誤,分母為0的除法等,核心需要通知相應程式。

也可以由其他程式產生併傳送給核心,再由核心傳遞給目標程式。

訊號傳遞的過程

核心中針對每一個程式都有一個表來儲存訊號。

當核心需要將訊號傳遞給某個程式時,就在該程式對應的表中寫入訊號,這樣就生成了訊號。

當該程式由使用者態陷入核心態,再次切換到使用者態之前,會檢視錶中的訊號。如果有訊號,程式就會首先執行訊號對應的操作,此時叫做執行訊號。

從生成訊號到將訊號傳遞給對應程式這段時間,訊號處於等待狀態。

我們可以編寫程式碼,讓程式阻塞(block)某些訊號,也就是讓這些訊號始終處於等待的狀態,直到程式取消阻塞(unblock)或者忽略訊號。

訊號種類

一些常見訊號


訊號名稱數字表示說明
SIGHUP1終端掛起或控制程式終止。當使用者退出Shell時,由該程式啟動的所有程式都會收到這個訊號,預設動作為終止程式。
SIGINT2鍵盤中斷。當使用者按下組合鍵時,使用者終端向正在執行中的由該終端啟動的程式發出此訊號。預設動作為終止程式。 
SIGQUIT3鍵盤退出鍵被按下。當使用者按下組合鍵時,使用者終端向正在執行中的由該終端啟動的程式發出此訊號。預設動作為退出程式。
SIGILL4非法指令
SIGABRT6由abort(3)發出的退出指令
SIGFPE8發生致命的運算錯誤時發出。不僅包括浮點運算錯誤,還包括溢位及除數為0等所有的演算法錯誤。預設動作為終止程式併產生core檔案。
SIGKILL9無條件終止程式。程式接收到該訊號會立即終止,不進行清理和暫存工作。該訊號不能被忽略、處理和阻塞,它向系統管理員提供了可以殺死任何程式的方法。
SIGSEGV11無效的記憶體引用
SIGALRM14定時器超時,預設動作為終止程式。
SIGTERM15程式結束訊號,可以由 kill 命令產生。與SIGKILL不同的是,SIGTERM 訊號可以被阻塞和終止,以便程式在退出前可以儲存工作或清理臨時檔案等。


使用例項

透過 kill -l 命令可以檢視系統支援的所有訊號:

$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE     14) SIGALRM     15) SIGTERM     17) SIGCHLD
18) SIGCONT     19) SIGSTOP     20) SIGTSTP     21) SIGTTIN
22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO
30) SIGPWR      31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1
36) SIGRTMIN+2  37) SIGRTMIN+3  38) SIGRTMIN+4  39) SIGRTMIN+5
40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8  43) SIGRTMIN+9
44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13
52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9
56) SIGRTMAX-8  57) SIGRTMAX-7  58) SIGRTMAX-6  59) SIGRTMAX-5
60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2  63) SIGRTMAX-1
64) SIGRTMAX

注意:上面是在CentOS下的命令執行結果,不同的Linux發行版支援的訊號可能不同。


每種訊號都會有一個預設動作。預設動作就是指令碼或程式接收到該訊號所做出的預設操作。常見的預設動作有終止程式、退出程式、忽略訊號、重啟暫停的程式等,上表中也對部分預設動作進行了說明。

傳送訊號

有多種方式可以向程式或指令碼傳送訊號,例如按下組合鍵會傳送SIGINT訊號,終止當前程式。

還可以透過 kill 命令傳送訊號,語法為:

$ kill -signal pid

signal為要傳送的訊號,可以是訊號名稱或數字;pid為接收訊號的程式ID。例如:

$ kill -1 35365

將SIGHUP訊號傳送給程式ID為35365的程式,程式會終止執行。

強制殺死ID為35365的程式:

$ kill -9 35365

捕獲訊號

通常情況下,直接終止程式並不是我們所希望的。例如,按下,程式被立即終止,不會清理建立的臨時檔案,帶來系統垃圾,也不會儲存正在進行的工作,導致需要重做。

可以透過程式設計來捕獲這些訊號,當終止訊號出現時,可以先進行清場和儲存處理,再退出程式。

透過 trap 命令就可以捕獲訊號,語法為:

$ trap commands signals

commands為Linux系統命令或使用者自定義命令;signals為要捕獲的訊號,可以為訊號名稱或數字。

捕獲到訊號後,可以有三種處理:

執行一段指令碼來做一些處理工作,例如清理臨時檔案;

接受(恢復)訊號的預設操作;

忽略當前訊號。

a. 清理臨時檔案

指令碼捕獲到終止訊號後一個常見的動作就是清理臨時檔案。例如:

$ trap "rm -f $WORKDIR/tmp$$ $WORKDIR/tmpTest$$; exit" 2

當使用者按下後,指令碼先清理臨時檔案 tmp$$ 和 tmpTest$$ 再退出。

注意:exit 命令是必須的,否則指令碼捕獲到訊號後會繼續執行而不是退出。

修改上面的指令碼,使接收到 SIGHUP 時進行同樣的操作:

$ trap "rm $WORKDIR/tmp$$ $WORKDIR/tmpTest$$; exit" 1 2

幾點注意:

如果執行多個命令,需要將命令用引號包圍;

只有指令碼執行到 trap 命令時才會捕獲訊號;

再次接收到訊號時還會執行同樣的操作。

上面的指令碼,執行到 trap 命令時就會替換 WORKDIR 和 $$ 的值。如果希望接收到 SIGHUP 或 SIGINT 訊號時再替換其值,那麼可以將命令放在單引號內,例如:

$ trap 'rm $WORKDIR/tmp$$ $WORKDIR/tmpTest$$; exit' 1 2

b. 忽略訊號

如果 trap 命令的 commands 為空,將會忽略接收到的訊號,即不做任何處理,也不執行預設動作。例如:

$ trap '' 2

也可以同時忽略多個訊號:

$ trap '' 1 2 3 15

注意:必須被引號包圍,不能寫成下面的形式:

$ trap  2

c. 恢復預設動作

如果希望改變訊號的預設動作後再次恢復預設動作,那麼省略 trap 命令的 commands 即可,例如:

$ trap 1 2

將恢復SIGHUP 和 SIGINT 訊號的預設動作。


相關文章