信号概念
信号是軟體終端,它提供了一種異步處理事件的方法。每個信号都有一個名字,這些名字都是以三個字元SIG開頭。
産生終端信号的條件:
1. 當使用者按某些終端鍵時,産生信号。
2. 硬體異常産生信号:除數為0、無效的存儲通路等等。
3. 程序用kill函數可将信号發送給另一個程序或程序組使用者可用kill指令将信号發送給其他程序。
4. 當檢測到某種軟體條件已經發生,并将其通知有關程序時也産生信号。
程序對信号的響應
程序可以通過三種方式來響應一個信号:
1. 忽略信号,即對信号不做任何處理,其中,有兩個信号不能忽略:SIGKILL及SIGSTOP;
2. 捕捉信号。定義信号處理函數,當信号發生時,執行相應的處理函數;
3. 執行預設操作。
信号的分類
非可靠信号
早期unix下的不可靠信号主要指的是程序可能對信号做出錯誤的反應以及信号可能丢失。
可靠信号
信号值位于SIGRTMIN和SIGRTMAX之間的信号都是可靠信号,可靠信号克服了信号可能丢失的問題。
信号的可靠與不可靠隻與信号值有關,與信号的發送及安裝函數無關。當然也可以稱為實時信号或者非實時信号,非實時信号都不支援排隊,都是不可靠信号;實時信号都支援排隊,都是可靠信号。
信号的生命周期
從信号發送到信号處理函數的執行完畢。對于一個完整的信号生命周期(從信号發送到相應的處理函數執行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:1)信号産生;2)信号在程序中注冊完畢;3)信号在程序中的登出完畢;4)信号處理函數執行完畢。相鄰兩個事件的時間間隔構成信号生命周期的一個階段。
當一個實時信号發送給一個程序時,不管該信号是否已經在程序中注冊,都會被再注冊一次,是以,信号不會丢失,是以,實時信号又叫做"可靠信号"。這意味着同一個實時信号可以在同一個程序的未決信号資訊鍊中占有多個sigqueue結構(程序每收到一個實時信号,都會為它配置設定一個結構來登記該信号資訊,并把該結構添加在未決信号鍊尾,即所有誕生的實時信号都會在目标程序中注冊);
當一個非實時信号發送給一個程序時,如果該信号已經在程序中注冊,則該信号将被丢棄,造成信号丢失。是以,非實時信号又叫做"不可靠信号"。這意味着同一個非實時信号在程序的未決信号資訊鍊中,至多占有一個sigqueue結構(一個非實時信号産生後,1如果發現相同的信号已經在目标結構中注冊,則不再注冊,對于程序來說,相當于不知道本次信号發生,信号丢失;2如果程序的未決信号中沒有相同信号,則在程序中注冊自己)。
需要注意的要點是:
1)信号注冊與否,與發送信号的函數(如kill()或sigqueue()等)以及信号安裝函數(signal()及sigaction())無關,隻與信号值有關(信号值小于SIGRTMIN的信号最多隻注冊一次,信号值在SIGRTMIN及SIGRTMAX之間的信号,隻要被程序接收到就被注冊)。
2)在信号被登出到相應的信号處理函數執行完畢這段時間内,如果程序又收到同一信号多次,則對實時信号來說,每一次都會在程序中注冊;而對于非實時信号來說,無論收到多少次信号,都會視為隻收到一個信号,隻在程序中注冊一次。
函數詳細介紹
信号的發送
發送信号的主要函數有:kill(),raise(),sigqueue(),alarm(),setitimer()以及abort()。
1、int kill(pid_t pid,int signo)
參數pid的值 信号的接收程序
pid>0 程序ID為pid的程序
pid=0 同一個程序組的程序
pid<0 pid!=-1 程序組ID為 -pid的所有程序
pid=-1 除發送程序自身外,所有程序ID大于1的程序
Sinno是信号值,當為0時(即空信号),實際不發送任何信号,但照常進行錯誤檢查,是以,可用于檢查目标程序是否存在,
以及目前程序是否具有向目标發送信号的權限(root權限的程序可以向任何程序發送信号,非root權限的程序隻能向屬于同一個session或者同一個使用者的程序發送信号)。
kill最常用于pid>0時的信号發送,調用成功傳回 0; 否則,傳回 -1。
2、int raise(int signo)
允許向程序自身發送信号。
3、int sigqueue(pid_t pid, int sig, const union sigval val)
調用成功傳回 0;否則,傳回 -1。
sigqueue()是比較新的發送信号系統調用,主要是針對實時信号提出的(當然也支援前32種),支援信号帶有參數,與函數sigaction()配合使用。
sigqueue的第一個參數是指定接收信号的程序ID,第二個參數确定即将發送的信号,第三個參數是一個聯合資料結構union sigval,指定了信号傳遞的參數,即通常所說的4位元組值。
typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;
sigqueue() 比 kill() 傳遞了更多的附加資訊,但 sigqueue() 隻能向一個程序發送信号,而不能發送信号給一個程序組。如果 sig 為 0 ,将會執行錯誤檢查,但實際上不發送任何信号, 0 值信号可用于檢查 pid 的有效性以及目前程序是否有權限向目标程序發送信号。
在調用 sigqueue 時, sigval_t 指定的資訊會拷貝到 3 參數信号處理函數( 3 參數信号處理函數指的是信号處理函數由 sigaction 安裝,并設定了 sa_sigaction 指針,稍後将闡述)的 siginfo_t 結構中,這樣信号處理函數就可以處理這些資訊了。由于 sigqueue 系統調用支援發送帶參數信号,是以比 kill() 系統調用的功能要靈活和強大得多。
注: sigqueue() 發送非實時信号時,第三個參數包含的資訊仍然能夠傳遞給信号處理函數; sigqueue() 發送非實時信号時,仍然不支援排隊,即在信号處理函數執行過程中到來的所有相同信号,都被合并為一個信号。
4 、 unsigned int alarm(unsigned int seconds)
專門為 SIGALRM 信号而設,在指定的時間 seconds 秒後,将向程序本身發送 SIGALRM 信号,又稱為鬧鐘時間。程序調用 alarm 後,任何以前的 alarm() 調用都将無效。如果參數 seconds 為零,那麼程序内将不再包含任何鬧鐘時間。
函數傳回是這樣的,如果調用 alarm ()前,程序中已經設定了鬧鐘時間,則傳回上一個鬧鐘時間的剩餘時間,否則傳回 0 。
5 、 int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
setitimer() 比 alarm 功能強大,支援 3 種類型的定時器:
ITIMER_REAL : 設定絕對時間;經過指定的時間後,核心将發送 SIGALRM 信号給本程序;
ITIMER_VIRTUAL 設定程式執行時間;經過指定的時間後,核心将發送 SIGVTALRM 信号給本程序;
ITIMER_PROF 設定程序執行以及核心因本程序而消耗的時間和,經過指定的時間後,核心将發送 ITIMER_VIRTUAL 信号給本程序;
Setitimer() 第一個參數 which 指定定時器類型(上面三種之一);第二個參數是結構 itimerval 的一個執行個體,結構 itimerval 。
結構 itimerval :
struct itimerval
{
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
struct timeval
{
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
第三個參數可不做處理。
Setitimer() 調用成功傳回 0 ,否則傳回 -1 。
6、 void abort(void);
向程序發送 SIGABORT 信号,預設情況下程序會異常退出,當然可定義自己的信号處理函數。
即使 SIGABORT 被程序設定為阻塞信号,調用 abort() 後, SIGABORT 仍然能被程序接收。該函數無傳回值。
信号的捕獲與安裝(設定信号關關聯作)
如果程序要處理某一信号,那麼就要在程序中安裝該信号。安裝信号主要用來确定信号值及程序針對該信号值的動作之間的映射關系,即程序将要處理哪個信号;該信号被傳遞給程序時,将執行何種操作。
主要有兩個函數實作信号的安裝:signal()、sigaction()。其中signal()在可靠信号系統調用的基礎上實作, 是庫函數。它隻有兩個參數,不支援信号傳遞資訊,主要是用于前32種非實時信号的安裝;而sigaction()是較新的函數(由兩個系統調用實作:sys_signal以及sys_rt_sigaction),有三個參數,支援信号傳遞資訊,主要用來與sigqueue() 系統調用配合使用,當然,sigaction()同樣支援非實時信号的安裝。sigaction()優于signal()主要展現在支援信号帶有參數。
1. void (*signal(int signum, void (*handler))(int)))(int);
如果該函數原型不容易了解的話,可以參考下面的分解方式來了解:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
第一個參數指定信号的值,第二個參數指定針對前面信号值的處理,
a,可以忽略該信号(參數設為SIG_IGN);
b,可以采用系統預設方式處理信号(參數設為SIG_DFL);
c,也可以自己實作處理方式(參數指定一個函數位址)。
如果signal()調用成功,傳回最後一次為安裝信号signum而調用signal()時的handler值;失敗則傳回SIG_ERR。
2. int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
sigaction函數用于改變程序接收到特定信号後的行為。該函數的第一個參數為信号的值,可以為除SIGKILL及SIGSTOP外的任何一個特定有效的信号(為這兩個信号定義自己的處理函數,将導緻信号安裝錯誤)。第二個參數是指向結構sigaction的一個執行個體的指針,在結構sigaction的執行個體中,指定了對特定信号的處理,可以為空,程序會以預設方式對信号處理;第三個參數oldact指向的對象用來儲存原來對相應信号的處理,可指定oldact為NULL。如果把第二、第三個參數都設為NULL,那麼該函數可用于檢查信号的有效性。
第二個參數最為重要,其中包含了對指定信号的處理、信号所傳遞的資訊、信号處理函數執行過程中應屏蔽掉哪些函數等等。
可重入函數
重入一般可以了解為一個函數同時被多個程式調用。可重入函數在任何時候都可以被中斷,而一段時間之後又可以恢複運作,而相應的資料不會破壞或者丢失。
一般可重入函數有三個條件:1)不适用靜态資料結構;2)不調用malloc或free;3)不是标準IO函數。
在信号的處理過程中調用一個不可重入的函數,其結果是不可預測的。