Linux信號機制與信號處理

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

與其他進程間通信方式(例如管道、共享內存等)相比,信號所能傳遞的信息比較粗糙,只是一個整數。但正是由於傳遞的信息量少,信號也便於管理和使用,可以用於系統管理相關的任務,例如通知進程終結、中止或者恢復等。

每種信號用一個整型常量宏表示,以SIG開頭,比如SIGCHLD、SIGINT等,它們在系統頭文件<signal.h>中定義。

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

  • 可以由內核自身產生,比如出現硬件錯誤、內存讀取錯誤,分母爲0的除法等,內核需要通知相應進程。

  • 也可以由其他進程產生併發送給內核,再由內核傳遞給目標進程。


信號傳遞的過程:

  • 內核中針對每一個進程都有一個表來保存信號。

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

  • 當該進程由用戶態陷入內核態,再次切換到用戶態之前,會查看錶中的信號。如果有信號,進程就會首先執行信號對應的操作,此時叫做執行信號。

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

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

信號種類

下表列出了一些常見信號:

信號名稱 數字表示 說明
SIGHUP 1 終端掛起或控制進程終止。當用戶退出Shell時,由該進程啓動的所有進程都會收到這個信號,默認動作爲終止進程。
SIGINT 2 鍵盤中斷。當用戶按下<Ctrl+C>組合鍵時,用戶終端向正在運行中的由該終端啓動的程序發出此信號。默認動作爲終止進程。 
SIGQUIT 3 鍵盤退出鍵被按下。當用戶按下<Ctrl+D>或<Ctrl+\>組合鍵時,用戶終端向正在運行中的由該終端啓動的程序發出此信號。默認動作爲退出程序。
SIGFPE 8 發生致命的運算錯誤時發出。不僅包括浮點運算錯誤,還包括溢出及除數爲0等所有的算法錯誤。默認動作爲終止進程併產生core文件。
SIGKILL 9 無條件終止進程。進程接收到該信號會立即終止,不進行清理和暫存工作。該信號不能被忽略、處理和阻塞,它向系統管理員提供了可以殺死任何進程的方法。
SIGALRM 14 定時器超時,默認動作爲終止進程。
SIGTERM 15 程序結束信號,可以由 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     16) SIGSTKFLT
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

上面僅是一個演示,不同的Linux發行版支持的信號可能不同。

每種信號都會有一個默認動作。默認動作就是腳本或程序接收到該信號所做出的默認操作。常見的默認動作有終止進程、退出程序、忽略信號、重啓暫停的進程等,上表中也對部分默認動作進行了說明。

發送信號

有多種方式可以向程序或腳本發送信號,例如按下<Ctrl+C>組合鍵會發送SIGINT信號,終止當前進程。

還可以通過 kill 命令發送信號,語法爲:

$ kill -signal pid

signal爲要發送的信號,可以是信號名稱或數字;pid爲接收信號的進程ID。例如:

$ kill -1 1001

將SIGHUP信號發送給進程ID爲1001的程序,程序會終止執行。

又如,強制殺死ID爲1001的進程:

$ kill -9 1001

捕獲信號

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

可以通過編程來捕獲這些信號,當終止信號出現時,可以先進行清場和保存處理,再退出程序。

用戶程序可以通過C/C++等代碼捕獲信號,這將在Linux C編程中進行講解,這裏僅介紹如果通過Linux命令捕獲信號。

通過 trap 命令就可以捕獲信號,語法爲:

$ trap commands signals

commands爲Linux系統命令或用戶自定義命令;signals爲要捕獲的信號,可以爲信號名稱或數字。

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

  • 執行一段腳本來做一些處理工作,例如清理臨時文件;

  • 接受(恢復)信號的默認操作;

  • 忽略當前信號。

1) 清理臨時文件

腳本捕獲到終止信號後一個常見的動作就是清理臨時文件。例如:

$ trap "rm -f $WORKDIR/work1$$ $WORKDIR/dataout$$; exit" 2

當用戶按下<Ctrl+C>後,腳本先清理臨時文件 work1$$ 和 dataout$$ 再退出。

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

修改上面的腳本,使接收到 SIGHUP 時進行同樣的操作:

$ trap "rm $WORKDIR/work1$$ $WORKDIR/dataout$$; exit" 1 2

幾點注意:

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

  • 只有腳本執行到 trap 命令時纔會捕獲信號;

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


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

$ trap 'rm $WORKDIR/work1$$ $WORKDIR/dataout$$; exit' 1 2

2) 忽略信號

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

$ trap '' 2

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

$ trap '' 1 2 3 15

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

$ trap  2

3) 恢復默認動作

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

$ trap 1 2

將恢復SIGHUP 和 SIGINT 信號的默認動作。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章