信號(signal)是Linux進程間通信的一種機制,全稱為軟中斷信號,也被稱為軟中斷。信號本質上是在軟件層次上對硬件中斷機制的一種模擬。
與其他進程間通信方式(例如管道、共享內存等)相比,信號所能傳遞的信息比較粗糙,只是一個整數。但正是由於傳遞的信息量少,信號也便於管理和使用,可以用於系統管理相關的任務,例如通知進程終結、中止或者恢復等。
每種信號用一個整型常量宏表示,以SIG開頭,比如SIGCHLD、SIGINT等,它們在系統頭文件<signal.h>中定義。
信號由內核(kernel)管理,產生方式多種多樣:
可以由內核自身產生,比如出現硬件錯誤、內存讀取錯誤,分母為0的除法等,內核需要通知相應進程。
也可以由其他進程產生並發送給內核,再由內核傳遞給目標進程。
信號傳遞的過程:
內核中針對每一個進程都有一個表來保存信號。
當內核需要將信號傳遞給某個進程時,就在該進程對應的表中寫入信號,這樣就生成了信號。
當該進程由用戶態陷入內核態,再次切換到用戶態之前,會查看表中的信號。如果有信號,進程就會首先執行信號對應的操作,此時叫做執行信號。
從生成信號到將信號傳遞給對應進程這段時間,信號處於等待狀態。
我們可以編寫代碼,讓進程阻塞(block)某些信號,也就是讓這些信號始終處於等待的狀態,直到進程取消阻塞(unblock)或者忽略信號。
$ 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發行版支持的信號可能不同。
每種信號都會有一個默認動作。默認動作就是腳本或程序接收到該信號所做出的默認操作。常見的默認動作有終止進程、退出程序、忽略信號、重啟暫停的進程等,上表中也對部分默認動作進行了說明。
還可以通過 kill 命令發送信號,語法為:
$ kill -signal pidsignal為要發送的信號,可以是信號名稱或數字;pid為接收信號的進程ID。例如:
$ kill -1 1001將SIGHUP信號發送給進程ID為1001的程序,程序會終止執行。
又如,強制殺死ID為1001的進程:
$ kill -9 1001
可以通過編程來捕獲這些信號,當終止信號出現時,可以先進行清場和保存處理,再退出程序。
用戶程序可以通過C/C++等代碼捕獲信號,這將在Linux C編程中進行講解,這裡僅介紹如果通過Linux命令捕獲信號。
通過 trap 命令就可以捕獲信號,語法為:
$ trap commands signalscommands為Linux系統命令或用戶自定義命令;signals為要捕獲的信號,可以為信號名稱或數字。捕獲到信號後,可以有三種處理:
執行一段腳本來做一些處理工作,例如清理臨時文件;
接受(恢復)信號的默認操作;
忽略當前信號。
$ 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
$ trap '' 2也可以同時忽略多個信號:
$ trap '' 1 2 3 15注意:必須被引號包圍,不能寫成下面的形式:
$ trap 2
$ trap 1 2將恢復SIGHUP 和 SIGINT 信號的默認動作。