信號機制
一個完整的信號生命週期可以分爲3個重要階段,這3個階段由4個重要事件來刻畫的:信號產生、信號在進程中註冊、信號在進程中註銷、執行信號處理函數
一個不可靠信號的處理過程是這樣的:如果發現該信號已經在進程中註冊,那麼忽略該信號。因此,若前一個信號還未註銷又產生了相同的信號會產生信號丟失。
而當可靠信號發送給一個進程時,不管該信號是否已經在進程中註冊,都會被再註冊一次,因此信號不會丟失。
所有可靠信號都支持排隊,而不可靠信號則都不支持排隊。
用戶進程對信號的響應可以有3種方式:
忽略信號,即對信號不做任何處理,但是有兩個信號不能忽略,即SIGKILL及SIGSTOP。
捕捉信號,定義信號處理函數,當信號發生時,執行相應的處理函數。
執行缺省操作,Linux對每種信號都規定了默認操作。
執行kill-l 可以得到系統的所有信號:
函數介紹:
kill函數不僅僅可以終止進程,(實際上是發出SIGKILL信號)也可以向進程發送其他信號。
man2 kill可以得到詳細的介紹:
含義:
sendsignal to a process
頭文件:
#include<sys/types.h>
#include<signal.h>
函數原型:
intkill(pid_t pid, int sig);
參數含義:
Ifpid is positive, then signal sig is sent to the process with the ID
specifiedby pid.
如果pid是正數,則pid是信號要發送給進程的進程號
Ifpid equals 0, then sig is sent to every process in the process group
ofthe calling process.
如果pid=0,信號發送給pid所在多進程組的所有進程。
Ifpid equals -1, then sig is sent to every process for which the call‐
ing process has permission to send signals, except for process 1
(init),but see below.
如果 pid=-1.發給所有進程中的進程。除了進程號最大的外
Ifpid is less than -1, then sig is sent to every process in the
processgroup whose ID is -pid.
pid<-1發送給進程ID=-pid的進程
返回值
Onsuccess (at least one signal was sent), zero is returned. On error,
-1is returned, and errno is set appropriately.
raise函數:
含義:
raise- send a signal to the caller (caller)
kill-- send signal to a process
頭文件:
#include<signal.h>
函數原型:
intraise(int sig);
在單進程程序中等於: kill(getpid(),sig);
在多進程程序中等於: pthread_kill(pthread_self(),sig);
Ifthe signal causes a handler to be called, raise() will only return
afterthe signal handler has returned.
返回值:
raise()returns 0 on success, and nonzero for failure.
例子:
下面這個示例首先使用fork創建了一個子進程,接着爲了保證子進程不在父進程調用kill之前退出,在子進程中使用raise函數向子進程發送SIGSTOP信號,使子進程暫停。
接下來再在父進程中調用kill向子進程發送信號,在該示例中使用的是SIGKILL
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h>
intmain()
{
pid_tpid;
intret;
printf("createchild process\n");
if((pid = fork())<0) {
perror("forkerror");
exit(1);
}
if(0 == pid) {
printf("Inchild process do raise() to send 'SIGSTOP' signal.\n\n");
raise(SIGSTOP);
exit(0);
}
else{
printf("Inparent process collect signal which were child process sent,\n");
printf("andcall kill() do options for itself.\n");
printf("pid= %d.\n", pid);
if(0 == (waitpid(pid,NULL,WNOHANG))) {/* return immediately if nochild has exited.
*/
if(0 == (ret=kill(pid,SIGKILL))) {
printf("kill%d\n",pid);
}
else{
perror("killerror\n");
}
}
}
return0;
}
alarm函數
含義:
alarm- set an alarm clock for delivery of a signal
頭文件:
#include<unistd.h>
函數原型:
unsignedint alarm(unsigned int seconds);
描述:
alarm() arranges for a SIGALRM signal to be delivered to the calling
processin seconds seconds.
在指定時間到了以後會發送SIGALRM信號。
Ifseconds is zero, no new alarm() is scheduled.
Inany event any previously set alarm() is canceled.
如果之前設置過alarm將會被取消
返回值:
alarm()returns the number of seconds remaining until any previously
scheduledalarm was due to be delivered, or zero if there was no previ‐
ouslyscheduled alarm.
pause函數:
pause- wait for signal
頭文件:
#include<unistd.h>
函數原型:
intpause(void);
描述:
pause() causes the calling process (or thread) to sleep until a signal
isdelivered that either terminates the process or causes the invocation of a signal-catching function.
返回值:
pause() only returns when a signal was caught and the signal-catching
functionreturned. In this case pause() returns -1, and errno is set
toEINTR.
信號處理
實際執行信號的處理動作稱爲信號遞達(Delivery),信號從產生到遞達之間的狀態,稱爲信號未決(Pending)。進程可以選擇阻塞(Block)某個信號。被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作。注意,阻塞和忽略是不同的,只要信號被阻塞就不會遞達,而忽略是在遞達之後可選的一種處理動作。
一種是使用簡單的signal函數,另一種是使用信號集函數組。
signal函數語法要點 |
||
所需頭文件 |
#include<signal.h> |
|
函數原型 |
void(*signal(int signum, void (*handler)(int)))(int)) |
|
函數參數 |
signum:指定信號 |
|
|
handler |
|
|
||
|
||
函數返回值 |
成功:以前的信號處理配置。 |
|
|
出錯:-1 |
可見,首先該函數原型整體指向一個無返回值帶一個整型參數的函數指針,也是信號的原始配置函數。接着該原型又帶有兩個參數,其中的第二個參數可以是用戶自定義的信號處理函數的函數指針。
typedefvoid sign(int);
sign *signal(int, handler*);
信號集函數組
涉及一系列函數。主要包括一下幾個:
sigemptyset:初始化信號集合爲空。
sigfillset:初始化信號集合爲所有信號的集合。
sigaddset:將指定信號加入到信號集合中去。
sigdelset:將指定信號從信號集中刪去。
sigismember:查詢指定信號是否在信號集合之中。
創建信號集合函數語法要點 |
|
所需頭文件 |
#include<signal.h> |
函數原型 |
intsigemptyset (sigset_t * set) |
|
|
intsigaddset (sigset_t * set, int signum) |
|
|
|
intsigismember (sigset_t * set, int signum) |
|
函數參數 |
set:信號集。 |
|
|
函數返回值 |
成功:0(sigismember成功返回1,失敗返回0) |
|
sigset_t類型對於每種信號用一個bit表示“有效”或“無效”狀態
函數sigemptyset初始化set所指向的信號集,使其中所有信號的對應bit清零,表示該信號集不
包含任何有效信號。函數sigfillset初始化set所指向的信號集,使其中所有信號的對應bit置
位,表示該信號集的有效信號包括系統支持的所有信號。注意,在使用sigset_t類型的變量之
前,一定要調用sigemptyset或sigfillset做初始化,使信號集處於確定的狀態。初始
化sigset_t變量之後就可以在調用sigaddset和sigdelset在該信號集中添加或刪除某種有效信
號。這四個函數都是成功返回0,出錯返回-1。sigismember是一個布爾函數,用於判斷一個信號
集的有效信號中是否包含某種信號,若包含則返回1,不包含則返回0,出錯返回-1。
Sigprocmask函數:
含義:
sigprocmask- examine and change blocked signals
讀取和改變進程的屏蔽字
頭文件:
#include<signal.h>
函數原型:
intsigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how取值:
SIG_BLOCK
Theset of blocked signals is the union of the current set and
theset argument.
類似:mask=mask|set
SIG_UNBLOCK
The signals in set are removed from the current set of blocked
signals. It is permissible to attempt to unblock a signal which
isnot blocked.
mask=mask&~set
SIG_SETMASK
Theset of blocked signals is set to the argument set.
mask=set
If oldset is non-NULL, the previous value of the signal mask is stored
inoldset.
Ifset is NULL, then the signal mask is unchanged (i.e., how is
ignored), but the current value of the signal mask is nevertheless
returnedin oldset (if it is not NULL).
如果調用sigprocmask解除了對當前若干個未決信號的阻塞,則在sigprocmask返回前,至少將其
中一個信號遞達。
Sigpending函數
#include<signal.h>
intsigpending(sigset_t *set);
讀取當前進程的未決信號集,通過set傳出。調用成功返回0出錯返回-1
例子:
#include<signal.h>
#include<stdio.h>
voidprintsigset(const sigset_t *set)
{
inti;
for(i=0;i<32;i++) {
if(sigismember(set,i)==1){
putchar('1');
}else
putchar('0');
}
puts("");
}
intmain()
{
sigset_ts,p;
sigemptyset(&s);
sigaddset(&s,SIGINT);
sigprocmask(SIG_BLOCK,&s,NULL);
while(1) {
sigpending(&p);
printsigset(&p);
sleep(1);
}
return0;
}
程序運行時,每秒鐘把各信號的未決狀態打印一遍,由於我們阻塞了SIGINT信號,按Ctrl-C將會
使SIGINT信號處於未決狀態,按Ctrl-\仍然可以終止程序,因爲SIGQUIT信號沒有阻塞。在信號處於阻塞狀態時,所發出的信號對進程不起作用
sigpaction函數語法要點 |
|
所需頭文件 |
#include<signal.h> |
函數原型 |
intsigaction (int signum, const struct sigaction * act, structsigaction * oldact) |
函數參數 |
signum:信號的值,可以爲除SIGKILL及SIGSTOP外的任何一個特定有效的信號。 |
|
|
oldact:保存原來對相應信號的處理。 |
|
函數返回值 |
成功:0 |
|
struct sigaction {
void (*sa_handler)(intsigno);
sigset_t sa_mask;
intsa_flags;
void(*sa_restore)(void);
}
sa_handler是一個函數指針,指定信號關聯函數,這裏除可以是用戶自定義的處理函外,還可以爲SIG_DFL(採用缺省的處理方式)或SIG_IGN(忽略信號)。它的處理函數只有一個參數,即信號值。
sa_mask是一個信號集,它可以指定在信號處理程序執行過程中哪些信號應當被阻塞,在調用信號捕獲函數之前,該信號集要加入到信號的信號屏蔽字中。
sa_flags中包含了許多標誌位,是對信號進行處理的各個選擇。它的常見可選值如下表所示:
常見信號的含義及其默認操作 |
|
SA_NODEFER/SA_NOMASK |
當捕捉到此信號時,在執行其信號捕捉函數時,系統不會自動阻塞此信號。 |
SA_NOCLDSTOP |
進程忽略子進程產生的任何SIGSTOP、SIGTSTP、SIGTTIN和SA_NOCLDSTOPSIGTTOU信號。 |
SA_RESTART |
可讓重啓的系統調用重新起作用。 |
SA_ONESHOT/SA_RESETHAND |
自定義信號只執行一次,在執行完畢後恢復信號的系統默認動作。 |
sigaction函數可以讀取和修改與指定信號相關聯的處理動作。
將sa_handler賦值爲常數SIG_IGN傳給sigaction表示忽略信號,賦值爲常數SIG_DFL表示執行系統默認動作,賦值爲一個函數指針表示用自定義函數捕捉信號,或者說向內核註冊了一個信號處理函數,該函數返回值爲void,可以帶一個int參數,通過參數可以得知當前信號的編號,這 樣就可以用同一個函數處理多種信號。
例子
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
//自定義函數
void my_func(int signum)
{
printf("If you wantto quit, please try SIGQUIT.\n");
}
int main()
{
sigset_t set,pendset;
struct sigactionaction1,action2;
//初始化
if (sigemptyset(&set)<0){
perror("sigemptyseterror");
}
//增加
if(sigaddset(&set,SIGQUIT)<0)
perror("sigaddseterror");
if(sigaddset(&set,SIGINT)<0)
perror("sigadddseterror");
//阻塞
if(sigprocmask(SIG_BLOCK,&set,NULL)<0)
perror("sigprocmaskerror");
else
{
printf("blocked \n");
sleep(10);
}
//取消阻塞
if(sigprocmask(SIG_UNBLOCK, &set, NULL) < 0)
perror("sigprocmaskerror");
else
printf("unblock.\n");
while (1) {
//發揮作用
if(sigismember(&set,SIGINT)) {
sigemptyset(&action1.sa_mask);
action1.sa_handler =my_func;
sigaction(SIGINT,&action1,NULL);
}else if(sigismember(&set,SIGQUIT)) {
sigemptyset(&action2.sa_mask);
action2.sa_handler =SIG_DFL;
sigaction(SIGTERM,&action2,NULL);
}
}
return 0;
}
該實例首先把SIGQUIT、SIGINT兩個信號加入信號集,然後將該信號集設爲阻塞狀態,並在該狀態下使程序暫停5秒。
接下來再將信號集設置爲阻塞狀態,再對這兩個信號分別操作,其中SIGQUIT執行默認操作,而SIGINT執行用戶自定義函數的操作。