linux c之信号signal处理机制
最近同事的程序設(shè)計(jì)過(guò)程中用到了Linux的signal機(jī)制,從而引發(fā)了我對(duì)Linux中signal機(jī)制的思考。Signal機(jī)制在Linux中是一個(gè)非常常用的進(jìn)程間通信機(jī)制,很多人在使用的時(shí)候不會(huì)考慮該機(jī)制是具體如何實(shí)現(xiàn)的。signal機(jī)制可以被理解成進(jìn)程的軟中斷,因此,在實(shí)時(shí)性方面還是相對(duì)比較高的。Linux中signal機(jī)制的模型可以采用下圖進(jìn)行描述。
個(gè)進(jìn)程都會(huì)采用一個(gè)進(jìn)程控制塊對(duì)其進(jìn)行描述,進(jìn)程控制塊中設(shè)計(jì)了一個(gè)signal的位圖信息,其中的每位與具體的signal相對(duì)應(yīng),這與中斷機(jī)制是保持一致的。當(dāng)系統(tǒng)中一個(gè)進(jìn)程A通過(guò)signal系統(tǒng)調(diào)用向進(jìn)程B發(fā)送signal時(shí),設(shè)置進(jìn)程B的對(duì)應(yīng)signal位圖,類(lèi)似于觸發(fā)了signal對(duì)應(yīng)中斷。發(fā)送signal只是“中斷”觸發(fā)的一個(gè)過(guò)程,具體執(zhí)行會(huì)在兩個(gè)階段發(fā)生:
1、? system call返回。進(jìn)程B由于調(diào)用了system call后,從內(nèi)核返回用戶態(tài)時(shí)需要檢查他擁有的signal位圖信息表,此時(shí)是一個(gè)執(zhí)行點(diǎn)。
2、? 中斷返回。進(jìn)程被系統(tǒng)中斷打斷之后,系統(tǒng)將CPU交給進(jìn)程時(shí),需要檢查即將執(zhí)行進(jìn)程所擁有的signal位圖信息表,此時(shí)也是一個(gè)執(zhí)行點(diǎn)。
?
綜上所述,signal的執(zhí)行點(diǎn)可以理解成從內(nèi)核態(tài)返回用戶態(tài)時(shí),在返回時(shí),如果發(fā)現(xiàn)待執(zhí)行進(jìn)程存在被觸發(fā)的signal,那么在離開(kāi)內(nèi)核態(tài)之后(也就是將CPU切換到用戶模式),執(zhí)行用戶進(jìn)程為該signal綁定的signal處理函數(shù),從這一點(diǎn)上看,signal處理函數(shù)是在用戶進(jìn)程上下文中執(zhí)行的。當(dāng)執(zhí)行完signal處理函數(shù)之后,再返回到用戶進(jìn)程被中斷或者system call(軟中斷或者指令陷阱)打斷的地方。
?
?????? Signal機(jī)制實(shí)現(xiàn)的比較靈活,用戶進(jìn)程由于中斷或者system call陷入內(nèi)核之后,將斷點(diǎn)信息都保存到了堆棧中,在內(nèi)核返回用戶態(tài)時(shí),如果存在被觸發(fā)的signal,那么直接將待執(zhí)行的signal處理函數(shù)push到堆棧中,在CPU切換到用戶模式之后,直接pop堆棧就可以執(zhí)行signal處理函數(shù)并且返回到用戶進(jìn)程了。Signal處理函數(shù)應(yīng)用了進(jìn)程上下文,并且應(yīng)用實(shí)際的中斷模擬了進(jìn)程的軟中斷過(guò)程。
?
最近寫(xiě)程序,各種bug各種錯(cuò),有一回程序莫名退出,沒(méi)報(bào)錯(cuò),也沒(méi)產(chǎn)生日志和core文件,貌似正常退出一樣。
但又不是在程序全部走完后退出,中途莫名退出,這就叫我想到了signal,應(yīng)該是某些函數(shù)錯(cuò)誤后發(fā)送kill信號(hào)給主進(jìn)程,然后退出。
現(xiàn)在總結(jié)下signal各種類(lèi)型:
| Signal | Description |
| SIGABRT | 由調(diào)用abort函數(shù)產(chǎn)生,進(jìn)程非正常退出 |
| SIGALRM | 用alarm函數(shù)設(shè)置的timer超時(shí)或setitimer函數(shù)設(shè)置的interval timer超時(shí) |
| SIGBUS | 某種特定的硬件異常,通常由內(nèi)存訪問(wèn)引起 |
| SIGCANCEL | 由Solaris Thread Library內(nèi)部使用,通常不會(huì)使用 |
| SIGCHLD | 進(jìn)程Terminate或Stop的時(shí)候,SIGCHLD會(huì)發(fā)送給它的父進(jìn)程。缺省情況下該Signal會(huì)被忽略 |
| SIGCONT | 當(dāng)被stop的進(jìn)程恢復(fù)運(yùn)行的時(shí)候,自動(dòng)發(fā)送 |
| SIGEMT | 和實(shí)現(xiàn)相關(guān)的硬件異常 |
| SIGFPE | 數(shù)學(xué)相關(guān)的異常,如被0除,浮點(diǎn)溢出,等等 |
| SIGFREEZE | Solaris專(zhuān)用,Hiberate或者Suspended時(shí)候發(fā)送 |
| SIGHUP | 發(fā)送給具有Terminal的Controlling Process,當(dāng)terminal被disconnect時(shí)候發(fā)送 |
| SIGILL | 非法指令異常 |
| SIGINFO | BSD signal。由Status Key產(chǎn)生,通常是CTRL+T。發(fā)送給所有Foreground Group的進(jìn)程 |
| SIGINT | 由Interrupt Key產(chǎn)生,通常是CTRL+C或者DELETE。發(fā)送給所有ForeGround Group的進(jìn)程 |
| SIGIO | 異步IO事件 |
| SIGIOT | 實(shí)現(xiàn)相關(guān)的硬件異常,一般對(duì)應(yīng)SIGABRT |
| SIGKILL | 無(wú)法處理和忽略。中止某個(gè)進(jìn)程 |
| SIGLWP | 由Solaris Thread Libray內(nèi)部使用 |
| SIGPIPE | 在reader中止之后寫(xiě)Pipe的時(shí)候發(fā)送 |
| SIGPOLL | 當(dāng)某個(gè)事件發(fā)送給Pollable Device的時(shí)候發(fā)送 |
| SIGPROF | Setitimer指定的Profiling Interval Timer所產(chǎn)生 |
| SIGPWR | 和系統(tǒng)相關(guān)。和UPS相關(guān)。 |
| SIGQUIT | 輸入Quit Key的時(shí)候(CTRL+\)發(fā)送給所有Foreground Group的進(jìn)程 |
| SIGSEGV | 非法內(nèi)存訪問(wèn) |
| SIGSTKFLT | Linux專(zhuān)用,數(shù)學(xué)協(xié)處理器的棧異常 |
| SIGSTOP | 中止進(jìn)程。無(wú)法處理和忽略。 |
| SIGSYS | 非法系統(tǒng)調(diào)用 |
| SIGTERM | 請(qǐng)求中止進(jìn)程,kill命令缺省發(fā)送 |
| SIGTHAW | Solaris專(zhuān)用,從Suspend恢復(fù)時(shí)候發(fā)送 |
| SIGTRAP | 實(shí)現(xiàn)相關(guān)的硬件異常。一般是調(diào)試異常 |
| SIGTSTP | Suspend Key,一般是Ctrl+Z。發(fā)送給所有Foreground Group的進(jìn)程 |
| SIGTTIN | 當(dāng)Background Group的進(jìn)程嘗試讀取Terminal的時(shí)候發(fā)送 |
| SIGTTOU | 當(dāng)Background Group的進(jìn)程嘗試寫(xiě)Terminal的時(shí)候發(fā)送 |
| SIGURG | 當(dāng)out-of-band data接收的時(shí)候可能發(fā)送 |
| SIGUSR1 | 用戶自定義signal 1 |
| SIGUSR2 | 用戶自定義signal 2 |
| SIGVTALRM | setitimer函數(shù)設(shè)置的Virtual Interval Timer超時(shí)的時(shí)候 |
| SIGWAITING | Solaris Thread Library內(nèi)部實(shí)現(xiàn)專(zhuān)用 |
| SIGWINCH | 當(dāng)Terminal的窗口大小改變的時(shí)候,發(fā)送給Foreground Group的所有進(jìn)程 |
| SIGXCPU | 當(dāng)CPU時(shí)間限制超時(shí)的時(shí)候 |
| SIGXFSZ | 進(jìn)程超過(guò)文件大小限制 |
| SIGXRES | Solaris專(zhuān)用,進(jìn)程超過(guò)資源限制的時(shí)候發(fā)送 |
signal對(duì)應(yīng)的值:
POSIX.1中列出的信號(hào):
SIGHUP 1 A 終端掛起或者控制進(jìn)程終止?
SIGINT 2 A 鍵盤(pán)中斷(如break鍵被按下)?
SIGQUIT 3 C 鍵盤(pán)的退出鍵被按下?
SIGILL 4 C 非法指令?
SIGABRT 6 C 由abort(3)發(fā)出的退出指令?
SIGFPE 8 C 浮點(diǎn)異常?
SIGKILL 9 AEF Kill信號(hào)?
SIGSEGV 11 C 無(wú)效的內(nèi)存引用?
SIGPIPE 13 A 管道破裂: 寫(xiě)一個(gè)沒(méi)有讀端口的管道?
SIGALRM 14 A 由alarm(2)發(fā)出的信號(hào)?
SIGTERM 15 A 終止信號(hào)?
SIGUSR1 30,10,16 A 用戶自定義信號(hào)1?
SIGUSR2 31,12,17 A 用戶自定義信號(hào)2?
SIGCHLD 20,17,18 B 子進(jìn)程結(jié)束信號(hào)?
SIGCONT 19,18,25 進(jìn)程繼續(xù)(曾被停止的進(jìn)程)?
SIGSTOP 17,19,23 DEF 終止進(jìn)程?
SIGTSTP 18,20,24 D 控制終端(tty)上按下停止鍵?
SIGTTIN 21,21,26 D 后臺(tái)進(jìn)程企圖從控制終端讀?
SIGTTOU 22,22,27 D 后臺(tái)進(jìn)程企圖從控制終端寫(xiě)?
沒(méi)在POSIX.1中列出,而在SUSv2列出
SIGBUS 10,7,10 C 總線錯(cuò)誤(錯(cuò)誤的內(nèi)存訪問(wèn))?
SIGPOLL A Sys V定義的Pollable事件,與SIGIO同義?
SIGPROF 27,27,29 A Profiling定時(shí)器到?
SIGSYS 12,-,12 C 無(wú)效的系統(tǒng)調(diào)用 (SVID)?
SIGTRAP 5 C 跟蹤/斷點(diǎn)捕獲?
SIGURG 16,23,21 B Socket出現(xiàn)緊急條件(4.2 BSD)?
SIGVTALRM 26,26,28 A 實(shí)際時(shí)間報(bào)警時(shí)鐘信號(hào)(4.2 BSD)?
SIGXCPU 24,24,30 C 超出設(shè)定的CPU時(shí)間限制(4.2 BSD)?
SIGXFSZ 25,25,31 C 超出設(shè)定的文件大小限制(4.2 BSD)?
(對(duì)于SIGSYS,SIGXCPU,SIGXFSZ,以及某些機(jī)器體系結(jié)構(gòu)下的SIGBUS,Linux缺省的動(dòng)作是A (terminate),SUSv2 是C (terminate and dump core))。?
下面是其它的一些信號(hào)?信號(hào) 值 處理動(dòng)作 發(fā)出信號(hào)的原因?
----------------------------------------------------------------------?
SIGIOT 6 C IO捕獲指令,與SIGABRT同義?
SIGEMT 7,-,7?
SIGSTKFLT -,16,- A 協(xié)處理器堆棧錯(cuò)誤?
SIGIO 23,29,22 A 某I/O操作現(xiàn)在可以進(jìn)行了(4.2 BSD)?
SIGCLD -,-,18 A 與SIGCHLD同義?
SIGPWR 29,30,19 A 電源故障(System V)?
SIGINFO 29,-,- A 與SIGPWR同義?
SIGLOST -,-,- A 文件鎖丟失?
SIGWINCH 28,28,20 B 窗口大小改變(4.3 BSD, Sun)?
SIGUNUSED -,31,- A 未使用的信號(hào)(will be SIGSYS)?
(在這里,- 表示信號(hào)沒(méi)有實(shí)現(xiàn);有三個(gè)值給出的含義為,第一個(gè)值通常在Alpha和Sparc上有效,中間的值對(duì)應(yīng)i386和ppc以及sh,最后一個(gè)值對(duì)應(yīng)mips。信號(hào)29在Alpha上為SIGINFO / SIGPWR ,在Sparc上為SIGLOST。)?
處理動(dòng)作一項(xiàng)中的字母含義如下?
A 缺省的動(dòng)作是終止進(jìn)程?
B 缺省的動(dòng)作是忽略此信號(hào)?
C 缺省的動(dòng)作是終止進(jìn)程并進(jìn)行內(nèi)核映像轉(zhuǎn)儲(chǔ)(dump core)?
D 缺省的動(dòng)作是停止進(jìn)程?
E 信號(hào)不能被捕獲?
F 信號(hào)不能被忽略
代碼測(cè)試
#include<stdio.h> #include<signal.h> #include<unistd.h> #include<stdlib.h> void when_alarm(); void when_sigint(); void when_sigchld(int); void when_sigusr1(); void when_sigio(); int main() { int childpid;//子程序進(jìn)程ID號(hào) printf("程序已經(jīng)開(kāi)始運(yùn)行,5秒鐘后將接收到時(shí)鐘信號(hào)。/n"); if ((childpid=fork())>0)//父進(jìn)程 { signal(SIGALRM,when_alarm); //當(dāng)接收到SIGALRM信號(hào)時(shí),調(diào)用when_alarm函數(shù) signal(SIGINT,when_sigint); //當(dāng)接收到SIGINT信號(hào)時(shí),調(diào)用when_sigint函數(shù) signal(SIGCHLD,when_sigchld);//當(dāng)接收到SIGCHLD信號(hào)時(shí),調(diào)用when_sigchld函數(shù) signal(SIGUSR1,when_sigusr1);//當(dāng)接收到SIGUSR1信號(hào)時(shí),調(diào)用when_sigusr1函數(shù) signal(SIGIO,when_sigio);//當(dāng)接收到SIGIO信號(hào)時(shí),調(diào)用when_sigio函數(shù) alarm(5); //5秒鐘之后產(chǎn)生SIGALRM信號(hào) raise(SIGIO); //向自己發(fā)送一個(gè)SIGIO信號(hào) pause(); //將父進(jìn)程暫停下來(lái),等待SIGALRM信號(hào)到來(lái) pause(); //將父進(jìn)程暫停下來(lái),等待SIGUSR1信號(hào)到來(lái) pause(); //將父進(jìn)程暫停下來(lái),等待SIGCHLD信號(hào)到來(lái) printf("------此時(shí)程序會(huì)停下來(lái)等待,請(qǐng)按下ctrl+c送出SIGINT信號(hào)-------/n"); pause(); //將父進(jìn)程暫停下來(lái),等待SIGINT信號(hào)到來(lái) } else if(childpid==0) //子進(jìn)程 { int timer; for(timer=7;timer>=0;timer--) //時(shí)鐘計(jì)時(shí)5秒產(chǎn)生SIGALRM信號(hào),再過(guò)2秒子進(jìn)程退出,產(chǎn)生SIGCHLD信號(hào) { if(timer>2) printf("距離SIGALRM信號(hào)到來(lái)還有%d秒。/n",timer-2); if(timer==4) kill(getppid(),SIGUSR1); //向父進(jìn)程發(fā)送一個(gè)SIGUSR1信號(hào) if((timer<=2)&&(timer>0)) printf("子進(jìn)程還剩%d秒退出,屆時(shí)會(huì)產(chǎn)生SIGCHLD信號(hào)。/n",timer); if(timer==0) //子進(jìn)程退出,產(chǎn)生SIGCHLD信號(hào) raise(SIGKILL); //子進(jìn)程給自己發(fā)一個(gè)結(jié)束信號(hào) sleep(1); //每個(gè)循環(huán)延時(shí)1秒鐘 } } else printf("fork()函數(shù)調(diào)用出現(xiàn)錯(cuò)誤!/n"); return 0; } void when_alarm() { printf("5秒鐘時(shí)間已到,系統(tǒng)接收到了SIGALRM信號(hào)!/n"); } void when_sigint() { printf("已經(jīng)接收到了SIGINT信號(hào),程序?qū)⑼顺?#xff01;/n"); exit(0); } void when_sigchld(int SIGCHLD_num) { printf("收到SIGCHLD信號(hào),表明我的子進(jìn)程已經(jīng)中止,SIGCHLD信號(hào)的數(shù)值是:%d。/n",SIGCHLD_num); } void when_sigusr1() { printf("系統(tǒng)接收到了用戶自定義信號(hào)SIGUSR1。/n"); } void when_sigio() { printf("系統(tǒng)接收到了SIGIO信號(hào)。/n"); }
總結(jié)
以上是生活随笔為你收集整理的linux c之信号signal处理机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: linux之内核剖析
- 下一篇: linux之od命令总结