信號機制

信號機制


一個完整的信號生命週期可以分爲3個重要階段,這3個階段由4個重要事件來刻畫的:信號產生、信號在進程中註冊、信號在進程中註銷、執行信號處理函數


一個不可靠信號的處理過程是這樣的:如果發現該信號已經在進程中註冊,那麼忽略該信號。因此,若前一個信號還未註銷又產生了相同的信號會產生信號丟失。


而當可靠信號發送給一個進程時,不管該信號是否已經在進程中註冊,都會被再註冊一次,因此信號不會丟失。


所有可靠信號都支持排隊,而不可靠信號則都不支持排隊。

用戶進程對信號的響應可以有3種方式:

忽略信號,即對信號不做任何處理,但是有兩個信號不能忽略,即SIGKILLSIGSTOP

捕捉信號,定義信號處理函數,當信號發生時,執行相應的處理函數。

執行缺省操作,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:信號集。


函數返回值

成功:0sigismember成功返回1,失敗返回0


sigset_t類型對於每種信號用一個bit表示“有效”或“無效”狀態

函數sigemptyset初始化set所指向的信號集,使其中所有信號的對應bit清零,表示該信號集不

包含任何有效信號。函數sigfillset初始化set所指向的信號集,使其中所有信號的對應bit

,表示該信號集的有效信號包括系統支持的所有信號。注意,在使用sigset_t類型的變量之

,一定要調用sigemptysetsigfillset做初始化,使信號集處於確定的狀態。初始

sigset_t變量之後就可以在調用sigaddsetsigdelset在該信號集中添加或刪除某種有效信

號。這四個函數都是成功返回0,出錯返回-1sigismember是一個布爾函數,用於判斷一個信號

集的有效信號中是否包含某種信號,若包含則返回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:信號的值,可以爲除SIGKILLSIGSTOP外的任何一個特定有效的信號。


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

進程忽略子進程產生的任何SIGSTOPSIGTSTPSIGTTINSA_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;

}

該實例首先把SIGQUITSIGINT兩個信號加入信號集,然後將該信號集設爲阻塞狀態,並在該狀態下使程序暫停5秒。


接下來再將信號集設置爲阻塞狀態,再對這兩個信號分別操作,其中SIGQUIT執行默認操作,而SIGINT執行用戶自定義函數的操作。


發佈了50 篇原創文章 · 獲贊 7 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章