Linux信号 一 信号可靠性与分类
開發SNMP的時候用到了Linux信號機制,總結了一下關于信號的知識。
信號是一種進程間通信手段,本質是一種軟件中斷,用來處理異步事件。信號機制是Unix家族里一個古老的通信機制。傳統的信號機制有一些弊端,更為嚴重的是信號處理函數的執行流和正常的執行流同時存在,這可能會對軟件運行帶來一定問題。
目錄
1. 信號的生命周期
2. 信號的產生
3. 信號的處理
4. 信號的分類
5. 信號的可靠性
1. 信號的生命周期
當向目標進程發送一枚信號SIGXXX時,Linux內核收到了產生的信號,然后再目標進程的進程描述符里記錄了一筆:收到了信號SIGXXX,但是還沒有遞送給目標進程的這一段時間里,信號處于掛起狀態,被稱為( pending )信號,也稱為未決信號。內核將信號遞送給進程,進程就會暫停當前的控制流,轉而去執行信號處理函數,這就是一個信號的完整生命周期。
典型信號可以會按照上面所述流程來處理,但是實際情況要復雜的多,還有許多場景需要考慮:
-
目標進程正在執行關鍵代碼,不能被信號中斷,需要阻塞某些信號,那么在這期間,信號就不允許被遞送到進程,直到目標進程接觸阻塞。
-
內核發現同一個信號已經存在,那么它該如何處理這種重復的信號,排隊還是丟棄?
-
內核遞送信號的時候,發現已有多個不同的信號被掛起,那它應該優先遞送哪個信號?
-
對于多線程的進程,如果向該進程發送信號,應該由哪一個線程來負責響應?
上述場景都是使用信號前需要考慮的問題。
2. 信號的產生
作為進程間通信的一種手段,進程之間可以互相發送信號,然而通常發給進程的信號,通常源于內核,包括:硬件異常、終端相關的信號和軟件事件相關的信號。
1. 硬件異常相關的信號:
信號 信號值 說明 SIGBUS | 7 | 總線錯誤,表示發生了內存訪問錯誤 SIGFPE | 8 | 表示發生了算術錯誤 SIGILL | 9 | 進程嘗試執行非法的機器語言指令 SIGSEGV | 11 | 段錯誤,表示應用程序訪問了無效地址這四種硬件異常,一般是由程序自身引發的,不是由其他進程發送的信號引發的,并且這些異常都比較致命,以至于進程無法繼續下去,所以這些信號產生之后,會立即遞送給進程。默認情況下,這四種信號都會使進程終止,并且產生core dump文件以供調試。對于這些信號,進程既不能忽略,也不能阻塞。2. 終端相關的信號
終端定義了如下幾種信號生成字符: Ctrl+C : 產生SIGINT信號 Ctrl+\ : 產生SIGQUIT信號 Ctrl+Z : 產生SIGTSTP信號 鍵入這些信號生成字符,相當于向前臺進程組發送了對應的信號。另一個和終端關系比較密切的信號是SIGHUP信號。很多程序員都遇到過這種問題:使用ssh登陸到遠程的Linux服務器,執行比較耗時的操作(如編譯項目代碼),卻因為網絡不穩定,或者需要關機回家,ssh連接被斷開,最終導致操作中途被放棄而失敗。之所以會這樣,是因為一個控制進程在失去其終端之后,內核會負責向其發送一個SIGHUP信號。在登錄會話中,shell通常是終端的控制進程,控制進程收到SIGHUP信號后,會引發如下的連鎖反應:shell收到SIGHUP后會終止,但是在終止之前,會向由shell創建的前臺進程組和后臺進程組發送SIGHUP信號,為了防止處于停止狀態的任務接收不到SIGHUP信號,通常會在SIGHUP信號之后,發送SIGCONT信號,喚醒處于停止狀態的任務。前臺進程組和后臺進程組的進程收到SIGHUP信號,默認的行為是終止進程,這也是前面提到的耗時任務中途失敗的原因。注意,單純地將命令放入后臺執行(通過&符號),并不能擺脫被SIGHUP信號追殺的命運。3. 軟件事件相關的信號
軟件事件觸發信號產生的情況比較多: 1. 子進程退出,內核可能會向父進程發送SIGCHLD信號 2. 父進程退出,內核可能會給子進程發送信號 3. 定時器到期,給進程發送信號。3. 信號的處理
從上面可以看到,信號產生的源頭有很多,那么內核將信號遞送給進程后,進程會執行什么操作呢?
很多信號尤其是傳統的信號,都會有默認的信號處理方式,如果我們不改變信號的處理函數,那么收到信號之后,就會執行默認的操作。信號的默認操作有一下幾種:
1. ignore : 顯示地忽略信號,內核將會丟棄該信號,信號不會對目標進程產生任何影響。2. terminate : 終止進程,很多信號的默認處理是終止進程。3. core : 生成核心轉儲文件并終止進程,進程會被殺死并且產生核心轉儲文件。核心轉儲文件記錄了進程死亡現場的信息,用戶可以使用核心轉儲文件來調試,分析進程死亡的原因。4. stop : 停止進程不同于終止進程,終止進程是進程已經死亡,但是停止進程僅是使進程暫停,將進程的狀態設置成TASK_STOPPED,一旦收到恢復執行的信號,進程還可以繼續執行。5. continue : 恢復進程的執行,和停止進程相對應,某些信號可以使進程恢復執行。根據信號的默認處理,可以將傳統信號進行如下分類:1. ignore類別 ----------------------------------------------------------- | 信號 | 值 | 說明 | ----------------------------------------------------------- | SIGCHLD | 17 | 子進程終止、停止或恢復執行 | ----------------------------------------------------------- | SIGURG | 23 | 套接字上的緊急數據 | ----------------------------------------------------------- | SIGWINCH | 28 | 終端窗口大小發生變化 | -----------------------------------------------------------2. terminate類別 ----------------------------------------------------------- | 信號 | 值 | 說明 | ----------------------------------------------------------- | SIGHUP | 1 | 掛起(hangup),多用于終端斷開 ----------------------------------------------------------- | SIGINT | 2 | 終端中斷 ----------------------------------------------------------- | SIGKILL | 9 | 殺死進程,該信號不能被忽略、屏蔽,該信號處 | | | 理函數也不能被用戶自定義函數該別 ----------------------------------------------------------- | SIGUSR1 | 10 | 用戶自定義信號1 ----------------------------------------------------------- | SIGUSR2 | 12 | 用戶自定義信號2 ----------------------------------------------------------- | SIGPIPE | 13 | 管道斷開,多見于socket通信 ----------------------------------------------------------- | SIGALAM | 14 | 定時器到期,該信號多用于實現定時器 ----------------------------------------------------------- | SIGTERM | 15 | 終止進程,因為SIGKILL過于殘暴,進程終止時 | | |可能需要先執行一些操作來保存現場信息,所以 | | |合理的殺死進程的方法是先發送SIGTERM信號,稍 | | |等片刻在發送SIGKILL信號 ----------------------------------------------------------- | SIGSTKFLT | 16 | 協處理器棧錯誤,Linux并未使用該信號 ----------------------------------------------------------- | SIGVTALRM | 26 | 虛擬定時器過期,setitimer函數的ITIMER_VIRTUAL模式 ----------------------------------------------------------- | SIGPROF | 27 | 性能分析定時器過期,setitimer函數的ITIMER_PROF模式 ----------------------------------------------------------- | SIGIO | 29 | I/O時可能發生 ----------------------------------------------------------- | SIGPWR | 30 | 電量將要耗盡 -----------------------------------------------------------3. core類別 ----------------------------------------------------------- | 信號 | 值 | 說明 | ----------------------------------------------------------- | SIGQUIT | 3 | 終端Ctrl+\可產生該信號 ----------------------------------------------------------- | SIGILL | 4 | 非法的指令 ----------------------------------------------------------- | SIGTRAP | 5 | 跟蹤/斷點,gdb/strace一類工具會使用該信號, | | | 這類工具會攔截或修改SIGTRAP信號處理函數 ----------------------------------------------------------- | SIGABRT | 6 | 進程終止,進程調用abort函數會向自身發送 | | | SIGABRT信號,此外如果使用了assert,assert | | | 失敗時也會產生SIGABRT信號 ----------------------------------------------------------- | SIGBUS | 7 | 總線錯誤 ----------------------------------------------------------- | SIGFPE | 8 | 算術異常 ----------------------------------------------------------- | SIGSEGV | 11 | 段錯誤,訪問了非法的地址 ----------------------------------------------------------- | SIGXCPU | 14 | 突破了對CPU時間的限制 ----------------------------------------------------------- | SIGXFSZ | 25 | 突破了對文件大小的限制 ----------------------------------------------------------- | SIGSYS | 31 | 無效的系統調用 -----------------------------------------------------------4. stop類別 ----------------------------------------------------------- | 信號 | 值 | 說明 | ----------------------------------------------------------- | SIGSTOP | 19 | 確保進程會停止,該信號不能被忽略,不能將信號 | | |信號處理函數改寫成用戶指定的函數 ----------------------------------------------------------- | SIGTSTP | 20 | 終端停止信號,和SIGSTOP功能類似,但是可以被 | | |進程忽略,可以被捕捉執行用戶指定的信號處理函數 ----------------------------------------------------------- | SIGTTIN | 21 | 用于作業控制,如果后臺進程組嘗試對終端執行 | | | read操作,終端驅動程序就會向該進程組發送 | | |SIGTTIN信號 ----------------------------------------------------------- | SIGTTOU | 22 | 如果終端啟動了TOSTOP(如通過stty tostop命令) | | | 即不允許后臺進程向終端寫入,而某一后臺進程 | | | 嘗試寫入終端時,終端驅動程序就會向進程組發送 | | | SIGTTOU信號 -----------------------------------------------------------5. continue類別 ----------------------------------------------------------- | 信號 | 值 | 說明 | ----------------------------------------------------------- | SIGCONT | 18 | 如果目標進程處于停止狀態,則恢復執行 -----------------------------------------------------------4. 信號的分類
在Linux的shell終端,執行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上面并沒有列出32號信號和33號信號,這兩個信號(SIGCANCEL 和 SIGSETXID)被NPTL這個線程庫征用了, 用來實現線程的取消。從內核層來說,32號信號應該是最小的實時信號(SIGRTMIN),但是由于32號和33號 被glibc內部征用了,所以glibc將SIGRTMIN設置成了34號信號。?
?
?
5. 信號的可靠性
信號可以分為兩類:
- 可靠信號 :信號值在[ 1, 31 ]之間的所有信號,被稱為不可靠信號;
- 不可靠信號 :在[ SIGRTMIN, SIGRTMAX]之間的信號被稱為可靠信號;
不可靠信號是指發送的信號,內核不一定能遞送給目標進程,信號可能會丟失。因為不可靠信號出現較早且應用廣泛,處于兼容性考慮,不能改變這些信號的行為模式,所以只能新增信號。新增的信號就是[SIGRTMIN, SIGRTMAX]范圍內的信號,它們被稱為可靠信號。
可靠信號和不可靠信號的根本差異在于收到信號后,內核有不同的處理方式。
對于不可靠信號,內核用位圖來記錄該信號是否處于掛起狀態。如果收到某不可靠信號,內核發現已經存在該信號處于未決狀態,就會簡單地丟棄該信號。因此發送不可靠信號,信號可能會丟失,即內核遞送給目標進程的次數,可能小于信號發送的次數。
對于可靠信號,內核內部有隊列來維護,如果收到可靠信號,內核會將信號掛到相應的隊列中,因此不會丟棄。嚴格手來,內核也設有上限,掛起信號的個數也不能無限制地增大,不然耗費內核資源,因此只能說,在一定范圍之內,可靠信號不會被丟棄。
可以通過如下命令獲取內核對掛起信號的限制值,不同的系統會有不一樣的值
ulimit -a從下圖可以看到,信號掛起隊列的限制值是7713
?
參考資料:
1. man signal http://www.man7.org/linux/man-pages/man7/signal.7.html
2. 《Linux環境編程 從應用到內核》高峰,李彬
總結
以上是生活随笔為你收集整理的Linux信号 一 信号可靠性与分类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Golang Study 三 map的顺
- 下一篇: 系统怎么设置c盘启动 &quot