信號就是軟中斷。
信號提供了異步處理事件的一種方式。例如,用戶在終端按下結束進程鍵,使一個進程提前終止。
?
1 信號的概念每一個信號都有一個名字,它們的名字都以SIG打頭。例如,每當進程調用了abort函數時,都會產生一個SIGABRT信號。
每一個信號對應一個正整數,定義在頭文件<signal.h>中。
沒有信號對應整數0,kill函數使用信號編號0表示一種特殊情況,所以信號編號0又叫做空信號(null signal)。
下面的各種情況會產生一個信號:
對于進程來說,信號是隨機產生的,所以進程不能簡單地根據檢測某個變量是否改變來判斷信號是否發生,而應該告訴內核“當這個信號發生時,做下面的這些事情”。
我們告訴內核當某個信號發生時做的事情叫做信號處理函數。信號處理函數有三種功能可供選擇:
?
對于一些信號發生時,會造成進程終止,同時生成一個core文件,該core文件記錄了該進程終止時的內存情況,可以幫助調試和調查進程的終止狀態。
有幾種情況不會生成core文件:
函數聲明
#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);
? ? Returns: PRevious disposition of signal if OK, SIG_ERR on err.
?函數聲明解析:
void ? (*signal(int ? signr, ? void ? (*handler)(int)))(int);?================================================?handler是一個函數指針,指向參數為單參數int,返回類型void的函數?signal是一個函數指針、這個函數指針指向一個參數為一個int型和一個handler型的指針、返回值是一個指向參數為int、返回值是void的函數的指針的指針。
總結一下:?? ? 這個復雜的聲明可以用下面2種比較簡單的型式表達出來,如下:?第一種型式如下: ? ??typedef ? void ? (*handler_pt)(int);?handler_pt ? signal1(int ? signum,handler_pt ? ahandler);?第二種型式如下:?typedef ? void ? handler_t(int);?handler_t* ? signal2(int ? signum, ? handler_t* ? ahandler);?------------------------------------------------------?以上這兩種形式結果是等價的,但也有區別,第一種形式定義的是函數指針類型,?sizeof(handler_pt)=4//borland ? c++ ? 5.6.4 ? for ? win ? 32,windos ? xp ? 32 ? platform?第二種形式定義的是函數類型,如果對他使用sizeof(handler_t)會提示:?sizeof ? may ? not ? be ? applied ? to ? a ? function
參數說明:
在上面的聲明解析中我們可以看到,使用typedef可以簡化signal函數的聲明,后面對signal函數的調用也將使用簡化后的聲明:
typedef void Sigfunc(int);
Sigfunc *signal(int, Sigfunc *);
?
?Example
該例子的作用是捕獲兩個用戶自定義的信號,并打印相關的信號信息。
使用函數pause來使程序掛起,知道接收到信號。
Code
#include "apue.h"
?
staticvoid sig_usr(int); ? /* one handler for both signals */
?? ? ? ? ?
int ? ? ?
main(void)
{ ? ? ? ?
? ? if (signal(SIGUSR1, sig_usr) == SIG_ERR)
? ? ? ? err_sys("can't catch SIGUSR1");
? ? if (signal(SIGUSR2, sig_usr) == SIG_ERR)
? ? ? ? err_sys("can't catch SIGUSR2");
? ? for ( ; ; )
? ? ? ? pause();
}? ?
?? ?
staticvoid
sig_usr(int signo)? ? ? /* argument is signal number */
{? ?
? ? if (signo == SIGUSR1)
? ? ? ? printf("received SIGUSR1/n");
? ? else if (signo == SIGUSR2)
? ? ? ? printf("received SIGUSR2/n");
? ??else
? ? ? ??err_dump("received signal %d/n", signo);
}
執行結果:
執行時,我們先讓該程序后臺執行,然后調用kill命令向該進程發送信號。
kill并不真的會殺死進程,而只是發送信號。所以kill并不是很準確的描述了該命令的作用。
當我們調用kill 2081命令時,進程被終止,因為在信號處理函數中并沒有處理該信號,而該信號的默認處理程序為終止進程。
?
程序啟動態程序執行時,所有信號的狀態都為默認值或者被忽略。
如果程序調用了exec系函數,則會改變信號的自定義處理函數為它的默認處理程序,因為在原來的程序中的處理函數地址對于新的程序來說是沒有意義的。
例如,在一個交互式的shell中,啟動一個后臺進程,會設置該進程的中斷和退出信號的處理動作為忽略,這樣,當用戶在shell中鍵入中斷命令時,只會中斷前臺進程,而不會影響后臺進程。
這個例子也告訴我們了signal函數的一個限制:我們無法確認當前進程的一些信號的處理動作,除非我們現在改變它們。后面我們將學習sigaction函數來確認一個信號的處理動作,而不需要改變它們。
?
程序創建當調用fork函數時,子進程繼承了父進程的信號處理函數。因為子進程拷貝了父進程的內存,所以信號處理函數的地址對于子進程來說也是有意義的。
?
3 不可靠的信號(Unreliable Signals)在早期的Unix系統中,信號是不可靠的。
不可靠的意思是,信號是有可能丟失的。即信號發生了,但是進程沒有捕獲它。
我們希望內核可以記住信號,當我們ready時,告訴我們該信號發生,讓我們去處理。
早期的系統,對于信號機制的實現還有一個問題:當信號發生,執行了信號處理函數,該信號的處理函數就被置為默認的信號處理程序。因此,早期的關于信號的程序框架如下所示:
這段代碼的問題在于,在SIGINT信號發生后,且在對它的信號處理函數重置為sig_int前,有一個時間差,在這個時間差內,可能再發生一次SIGINT信號。
如果第二次SIGINT發生在信號處理函數重置前,則會執行它的默認處理動作,即終止進程。
早期實現還有一個問題,就是如果進程不希望某個信號發生,它只能選擇忽略它,而無法將該信號關閉。
一種使用場景是:我們不希望被信號打斷,但是希望記住它們發生過。代碼可能如下:
在這里,我們假設該信號只發生一次。
代碼的目的在于:我們等待信號發生,信號發生之前,進程停止,等待。
代碼的問題在于,有一個時間差,可能會發生異常情況,如果代碼的執行序列如下:
1 信號發生
2 while (sig_int_flag == 0)
3 sig_int_flag= 1
4 pause()
這時,進程暫停掛起,等待信號發生,但是實際上該信號已經發生過了。這就導致了信號沒有被捕獲。
?
4 可中斷系統調用早期Unix操作系統的一個特性是:如果一個進程阻塞在一個“慢”系統調用,則該進程會收到一個信號,導致該進程被中斷。該系統調用返回一個錯誤,并且errno設置為EINTR。
系統調用被分為兩類:慢系統調用和其他系統調用。慢系統調用是那些可能永久阻塞的系統調用。慢系統調用包括:
對于可中斷系統調用,我們需要在代碼中處理errno EINTR:
為了避免需要顯式處理可中斷系統調用,一些可中斷系統調用在發生阻塞時會自動重啟。
這些會自動重啟的可中斷系統調用包括:ioctl, read, readv, write, writev, wait和waitepid。
如果某些應用并不希望這些系統調用自動重啟,可以該系統調用單獨設置SA_RESTART。
?
5 可重入函數信號的發生導致程序的指令執行順序被打亂。
但是在信號處理函數中,無法知道原進程的執行情況。
如果原進程這個在分配內存或者釋放內存,或者調用了修改static變量的函數,并在信號處理函數中再次調用該函數,會發生不可預期的結果。
在信號處理函數中可以安全調用的函數稱為可重入函數,也叫做異步信號安全的函數。除了保證可重入,這些函數還會阻塞可能導致結果不一致的信號。
如果函數滿足下面的一種或者幾種條件,則說明是不可重入的函數:
?
6 SIGCLD語義一直容易混淆的兩個信號是SIGCLD和SIGCHLD。
SIGCLD來自System V,而SIGCHLD來自BSD和POSIX.1。
BSD SIGCHLD的語義:當該信號發生時,說明子進程的狀態發生了改變,這時我們需要調用wait函數確認狀態的變化。
對于System V系統中,對信號SIGCLD的處理說明如下:
?
7 可靠信號及其語義?我們先定義幾個信號相關的概念:
?可靠機制,不同的標準對于異常情況有不同的處理:
?
?參考資料:
《Advanced Programming in the UNIX Envinronment 3rd》
新聞熱點
疑難解答