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 }


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章