Linux 信號signal處理機制


http://my.oschina.net/chenliang165/blog/125825.

 

 

最近同事的程序設計過程中用到了Linux的signal機制,從而引發了我對Linux中signal機制的思考。Signal機制在Linux中是一個非常常用的進程間通信機制,很多人在使用的時候不會考慮該機制是具體如何實現的。signal機制可以被理解成進程的軟中斷,因此,在實時性方面還是相對比較高的。Linux中signal機制的模型可以采用下圖進行描述。

 

 

 

 

       每個進程都會采用一個進程控制塊對其進行描述,進程控制塊中設計了一個signal的位圖信息,其中的每位與具體的signal相對應,這與中斷機制是保持一致的。當系統中一個進程A通過signal系統調用向進程B發送signal時,設置進程B的對應signal位圖,類似於觸發了signal對應中斷。發送signal只是“中斷”觸發的一個過程,具體執行會在兩個階段發生:

1、  system call返回。進程B由於調用了system call后,從內核返回用戶態時需要檢查他擁有的signal位圖信息表,此時是一個執行點。

2、  中斷返回。進程被系統中斷打斷之后,系統將CPU交給進程時,需要檢查即將執行進程所擁有的signal位圖信息表,此時也是一個執行點。

 

綜上所述,signal的執行點可以理解成從內核態返回用戶態時,在返回時,如果發現待執行進程存在被觸發的signal,那么在離開內核態之后(也就是將CPU切換到用戶模式),執行用戶進程為該signal綁定的signal處理函數,從這一點上看,signal處理函數是在用戶進程上下文中執行的。當執行完signal處理函數之后,再返回到用戶進程被中斷或者system call(軟中斷或者指令陷阱)打斷的地方。

 

       Signal機制實現的比較靈活,用戶進程由於中斷或者system call陷入內核之后,將斷點信息都保存到了堆棧中,在內核返回用戶態時,如果存在被觸發的signal,那么直接將待執行的signal處理函數push到堆棧中,在CPU切換到用戶模式之后,直接pop堆棧就可以執行signal處理函數並且返回到用戶進程了。Signal處理函數應用了進程上下文,並且應用實際的中斷模擬了進程的軟中斷過程。

 

最近寫程序,各種bug各種錯,有一回程序莫名退出,沒報錯,也沒產生日志和core文件,貌似正常退出一樣。 

但又不是在程序全部走完后退出,中途莫名退出,這就叫我想到了signal,應該是某些函數錯誤后發送kill信號給主進程,然后退出。 

現在總結下signal各種類型: 

 

 

Signal
Description
SIGABRT 
由調用abort函數產生,進程非正常退出 
SIGALRM 
用alarm函數設置的timer超時或setitimer函數設置的interval timer超時 
SIGBUS 
某種特定的硬件異常,通常由內存訪問引起 
SIGCANCEL 
由Solaris Thread Library內部使用,通常不會使用 
SIGCHLD 
進程Terminate或Stop的時候,SIGCHLD會發送給它的父進程。缺省情況下該Signal會被忽略 
SIGCONT 
當被stop的進程恢復運行的時候,自動發送 
SIGEMT 
和實現相關的硬件異常 
SIGFPE 
數學相關的異常,如被0除,浮點溢出,等等 
SIGFREEZE 
Solaris專用,Hiberate或者Suspended時候發送 
SIGHUP 
發送給具有Terminal的Controlling Process,當terminal被disconnect時候發送 
SIGILL 
非法指令異常 
SIGINFO 
BSD signal。由Status Key產生,通常是CTRL+T。發送給所有Foreground Group的進程 
SIGINT 
由Interrupt Key產生,通常是CTRL+C或者DELETE。發送給所有ForeGround Group的進程 
SIGIO 
異步IO事件 
SIGIOT 
實現相關的硬件異常,一般對應SIGABRT 
SIGKILL 
無法處理和忽略。中止某個進程 
SIGLWP 
由Solaris Thread Libray內部使用 
SIGPIPE 
在reader中止之后寫Pipe的時候發送 
SIGPOLL 
當某個事件發送給Pollable Device的時候發送 
SIGPROF 
Setitimer指定的Profiling Interval Timer所產生 
SIGPWR 
和系統相關。和UPS相關。 
SIGQUIT 
輸入Quit Key的時候(CTRL+\)發送給所有Foreground Group的進程 
SIGSEGV 
非法內存訪問 
SIGSTKFLT 
Linux專用,數學協處理器的棧異常 
SIGSTOP 
中止進程。無法處理和忽略。 
SIGSYS 
非法系統調用 
SIGTERM 
請求中止進程,kill命令缺省發送 
SIGTHAW 
Solaris專用,從Suspend恢復時候發送 
SIGTRAP 
實現相關的硬件異常。一般是調試異常 
SIGTSTP 
Suspend Key,一般是Ctrl+Z。發送給所有Foreground Group的進程 
SIGTTIN 
當Background Group的進程嘗試讀取Terminal的時候發送 
SIGTTOU 
當Background Group的進程嘗試寫Terminal的時候發送 
SIGURG 
當out-of-band data接收的時候可能發送 
SIGUSR1 
用戶自定義signal 1 
SIGUSR2 
用戶自定義signal 2 
SIGVTALRM 
setitimer函數設置的Virtual Interval Timer超時的時候 
SIGWAITING 
Solaris Thread Library內部實現專用 
SIGWINCH 
當Terminal的窗口大小改變的時候,發送給Foreground Group的所有進程 
SIGXCPU 
當CPU時間限制超時的時候 
SIGXFSZ 
進程超過文件大小限制 
SIGXRES 
Solaris專用,進程超過資源限制的時候發送 

 

 

signal對應的值:

POSIX.1中列出的信號:

SIGHUP 1 A 終端掛起或者控制進程終止 
SIGINT 2 A 鍵盤中斷(如break鍵被按下) 
SIGQUIT 3 C 鍵盤的退出鍵被按下 
SIGILL 4 C 非法指令 
SIGABRT 6 C 由abort(3)發出的退出指令 
SIGFPE 8 C 浮點異常 
SIGKILL 9 AEF Kill信號 
SIGSEGV 11 C 無效的內存引用 
SIGPIPE 13 A 管道破裂: 寫一個沒有讀端口的管道 
SIGALRM 14 A 由alarm(2)發出的信號 
SIGTERM 15 A 終止信號 
SIGUSR1 30,10,16 A 用戶自定義信號1 
SIGUSR2 31,12,17 A 用戶自定義信號2 
SIGCHLD 20,17,18 B 子進程結束信號 
SIGCONT 19,18,25 進程繼續(曾被停止的進程) 
SIGSTOP 17,19,23 DEF 終止進程 
SIGTSTP 18,20,24 D 控制終端(tty)上按下停止鍵 
SIGTTIN 21,21,26 D 后台進程企圖從控制終端讀 
SIGTTOU 22,22,27 D 后台進程企圖從控制終端寫 

沒在POSIX.1中列出,而在SUSv2列出

SIGBUS 10,7,10 C 總線錯誤(錯誤的內存訪問) 
SIGPOLL A Sys V定義的Pollable事件,與SIGIO同義 
SIGPROF 27,27,29 A Profiling定時器到 
SIGSYS 12,-,12 C 無效的系統調用 (SVID) 
SIGTRAP 5 C 跟蹤/斷點捕獲 
SIGURG 16,23,21 B Socket出現緊急條件(4.2 BSD) 
SIGVTALRM 26,26,28 A 實際時間報警時鍾信號(4.2 BSD) 
SIGXCPU 24,24,30 C 超出設定的CPU時間限制(4.2 BSD) 
SIGXFSZ 25,25,31 C 超出設定的文件大小限制(4.2 BSD) 

(對於SIGSYS,SIGXCPU,SIGXFSZ,以及某些機器體系結構下的SIGBUS,Linux缺省的動作是A (terminate),SUSv2 是C (terminate and dump core))。 

下面是其它的一些信號 

信號 值 處理動作 發出信號的原因 
---------------------------------------------------------------------- 
SIGIOT 6 C IO捕獲指令,與SIGABRT同義 
SIGEMT 7,-,7 
SIGSTKFLT -,16,- A 協處理器堆棧錯誤 
SIGIO 23,29,22 A 某I/O操作現在可以進行了(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 未使用的信號(will be SIGSYS) 

(在這里,- 表示信號沒有實現;有三個值給出的含義為,第一個值通常在Alpha和Sparc上有效,中間的值對應i386和ppc以及sh,最后一個值對應mips。信號29在Alpha上為SIGINFO / SIGPWR ,在Sparc上為SIGLOST。) 

處理動作一項中的字母含義如下 
A 缺省的動作是終止進程 
B 缺省的動作是忽略此信號 
C 缺省的動作是終止進程並進行內核映像轉儲(dump core) 
D 缺省的動作是停止進程 
E 信號不能被捕獲 
F 信號不能被忽略 

 

測試代碼:

 

01 #include<stdio.h>  
02 #include<signal.h>  
03 #include<unistd.h>  
04 #include<stdlib.h>  
05 voidwhen_alarm();  
06 voidwhen_sigint();  
07 voidwhen_sigchld(int);  
08 voidwhen_sigusr1();  
09 voidwhen_sigio();  
10 intmain()  
11 {  
12     intchildpid;//子程序進程ID號  
13     printf("程序已經開始運行,5秒鍾后將接收到時鍾信號。/n");  
14     if((childpid=fork())>0)//父進程  
15     {  
16         signal(SIGALRM,when_alarm); //當接收到SIGALRM信號時,調用when_alarm函數  
17         signal(SIGINT,when_sigint); //當接收到SIGINT信號時,調用when_sigint函數  
18         signal(SIGCHLD,when_sigchld);//當接收到SIGCHLD信號時,調用when_sigchld函數  
19         signal(SIGUSR1,when_sigusr1);//當接收到SIGUSR1信號時,調用when_sigusr1函數  
20         signal(SIGIO,when_sigio);//當接收到SIGIO信號時,調用when_sigio函數  
21         alarm(5);    //5秒鍾之后產生SIGALRM信號  
22         raise(SIGIO);//向自己發送一個SIGIO信號  
23         pause();//將父進程暫停下來,等待SIGALRM信號到來  
24         pause();//將父進程暫停下來,等待SIGUSR1信號到來  
25         pause();//將父進程暫停下來,等待SIGCHLD信號到來  
26         printf("------此時程序會停下來等待,請按下ctrl+c送出SIGINT信號-------/n");  
27         pause();//將父進程暫停下來,等待SIGINT信號到來          
28     }  
29     elseif(childpid==0)//子進程  
30     {  
31         inttimer;  
32         for(timer=7;timer>=0;timer--)//時鍾計時5秒產生SIGALRM信號,再過2秒子進程退出,產生SIGCHLD信號  
33         {  
34             if(timer>2)      
35                 printf("距離SIGALRM信號到來還有%d秒。/n",timer-2);  
36             if(timer==4)  
37                 kill(getppid(),SIGUSR1);//向父進程發送一個SIGUSR1信號  
38             if((timer<=2)&&(timer>0))  
39                 printf("子進程還剩%d秒退出,屆時會產生SIGCHLD信號。/n",timer);  
40             if(timer==0)//子進程退出,產生SIGCHLD信號  
41                 raise(SIGKILL);//子進程給自己發一個結束信號  
42             sleep(1);//每個循環延時1秒鍾  
43         }          
44     }  
45     else  
46         printf("fork()函數調用出現錯誤!/n");  
47     return0;  
48 }  
49 voidwhen_alarm()  
50 {  
51     printf("5秒鍾時間已到,系統接收到了SIGALRM信號!/n");  
52 }  
53 voidwhen_sigint()  
54 {  
55     printf("已經接收到了SIGINT信號,程序將退出!/n");  
56     exit(0);  
57 }  
58 voidwhen_sigchld(intSIGCHLD_num)  
59 {  
60     printf("收到SIGCHLD信號,表明我的子進程已經中止,SIGCHLD信號的數值是:%d。/n",SIGCHLD_num);  
61 }  
62 voidwhen_sigusr1()  
63 {  
64     printf("系統接收到了用戶自定義信號SIGUSR1。/n");  
65 }  
66 voidwhen_sigio()  
67 {  
68     printf("系統接收到了SIGIO信號。/n");  
69 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM