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