信號是與必定的進程相聯繫的。也就是說,一個進程能夠決定在進程中對哪些信號進行什 麼樣的處理。例如,一個進程能夠忽略某些信號而只處理其餘一些信號;另外,一個進程還能夠選擇如何處理信號。總之,這些總與特定的進程相聯繫的。所以,首 先要創建其信號和進程的對應關係,這就是信號的安裝登記。程序員
Linux 主要有兩個函數實現信號的安裝登記:signal和sigaction。其中signal在系統調用的基礎上實現,是庫函數。它只有兩個參數,不支持信號 傳遞信息,主要是用於前32個非實時信號的安裝;而sigaction是較新的函數(由兩個系統調用實現:sys_signal以及 sys_rt_sigaction),有三個參數,支持信號傳遞信息,主要用來與sigqueue系統調用配合使用。固然,sigaction一樣支持非 實時信號的安裝,sigaction優於signal主要體如今支持信號帶有參數。編程
對 於應用程序自行處理的信號來講,信號的生命週期要通過信號的安裝登記、信號集操做、信號的發送和信號的處理四個階段。信號的安裝登記指的是在應用程序中, 安裝對此信號的處理方法。信號集操做的做用是用於對指定的一個或多個信號進行信號屏蔽,此階段對有些應用程序來講並不須要。信號的發送指的是發送信號,可 以經過硬件(如在終端上按下Ctrl-C)發送的信號和軟件(如經過kill函數)發送的信號。信號的處理指的是操做系統對接收信號進程的處理,處理方法 是先檢查信號集操做函數是否對此信號進行屏蔽,若是沒有屏蔽,操做系統將按信號安裝函數中登記註冊的處理函數完成對此進程的處理。數組
(1)函數說明函數
在signal函數中,有兩個形參,分別表明須要處理的信號編號值和處理信號函數的指針。它主要是用於前32種非實時信號的處理,不支持信號的傳遞信息。可是因爲使用簡單,易於理解,所以在許多場合被程序員使用。工具
對 於Unix系統來講,使用signal函數時,自定義處理信號函數執行一次後失效,對該信號的處理回到默認處理方式。下面以一個例子進行說明,例如一程序 中使用signal(SIGQUIT, my_func)函數調用,其中my_func是自定義函數。應用進程收到SIGQUIT信號時,會跳轉到自定義處理信號函數my_func處執行,執行 後信號註冊函數my_func失效,對SIGQUIT信號的處理回到操做系統的默認處理方式,當應用進程再次收到SIGQUIT信號時,會按操做系統默認 的處理方式進行處理(即再也不執行my_func處理函數)。而在Linux系統中,signal函數已被改寫,由sigaction函數封裝實現,則不存 在上述問題。測試
(2)signal函數原型ui
signal(設置信號處理方式)spa |
||
所需頭文件操作系統 |
#include指針 |
|
函數說明 |
設置信號處理方式。signal()會依參數signum指定的信號編號來設置該信號的處理函數。當指定的信號到達時就會跳轉到參數handler指定的函數執行 |
|
函數原型 |
void (*signal(int signum,void(* handler)(int)))(int) |
|
函數傳入值
|
signum |
指定信號編號 |
handle |
SIG_IGN:忽略參數signum指定的信號 |
|
SIG_DFL:將參數signum指定的信號重設爲核心預設的信號處理方式,即採用系統默認方式處理信號 |
||
自定義信號函數處理指針 |
||
函數返回值 |
成功 |
返回先前的信號處理函數指針 |
出錯 |
SIG_ERR(-1) |
|
附加說明 |
在Unix環境中,在信號發生跳轉到自定的handler處理函數執行後,系統會自動將此處理函數換回原來系統預設的處理方式,若是要改變此情形請改用sigaction函數。在Linux環境中不存在此問題 |
signal函數原型比較複雜,若是使用下面的typedef,則可以使其簡化。
typedef void sign(int);
sign *signal(int, handler *);
可見,該函數原型首先總體指向一個無返回值帶一個整型參數的函數指針,也就是信號的原始配置函數。接着該原型又帶有兩個參數,其中的第二個參數能夠是用戶自定義的信號處理函數的函數指針。對這個函數格式能夠不理解,但須要學會模仿使用。
(3) signal函數使用實例
該示例代表瞭如何使用signal函數進行安裝登記信號處理函數。當該信號發生時,登記的信號處理函數會捕捉到相應的信號,並作出給定的處理。這裏,my_func就是信號處理的函數指針。讀者還能夠將my_func改成SIG_IGN或SIG_DFL查看運行結果。
signal.c源代碼以下:
#include
#include
#include
/*自定義信號處理函數*/
void my_func(int sign_no)
{
if(sign_no==SIGINT)
printf("I have get SIGINT\n");
else if(sign_no==SIGQUIT)
printf("I have get SIGQUIT\n");
}
int main()
{
printf("Waiting for signal SIGINT or SIGQUIT \n ");
/*發出相應的信號,並跳轉到信號處理函數處*/
signal(SIGINT, my_func);
signal(SIGQUIT, my_func);
pause();
pause();
exit(0);
}
編譯 gcc signal.c –o signal。
執行 ./signal,執行結果以下:
Waiting for signal SIGINT or SIGQUIT
I have get SIGINT /*按下Ctrl+C,操做系統就會向進程發送SIGINT信號*/
I have get SIGQUIT /*按下Ctrl-\(退出),操做系統就會向進程發送SIGQUIT信號*/
(1)sigaction函數原型
sigaction函數用來查詢和設置信號處理方式,它是用來替換早期的signal函數。sigaction函數原型及說明以下:
sigaction(查詢和設置信號處理方式) |
所需頭文件 |
#include |
|
函數說明 |
sigaction()會依參數signum指定的信號編號來設置該信號的處理函數 |
|
函數原型 |
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact) |
|
函數傳入值
|
signum |
能夠指定SIGKILL和SIGSTOP之外的全部信號 |
act |
參數結構sigaction定義以下 struct sigaction { void (*sa_handler) (int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer) (void); } ① sa_handler:此參數和signal()的參數handler相同,此參數主要用來對信號舊的安裝函數signal()處理形式的支持 ② sa_sigaction:新的信號安裝機制,處理函數被調用的時候,不但能夠獲得信號編號,並且能夠獲悉被調用的緣由以及產生問題的上下文的相關信息。 ③ sa_mask:用來設置在處理該信號時暫時將sa_mask指定的信號擱置 ④ sa_restorer: 此參數沒有使用 ⑤ sa_flags:用來設置信號處理的其餘相關操做,下列的數值可用。可用OR 運算(|)組合 ? A_NOCLDSTOP:若是參數signum爲SIGCHLD,則當子進程暫停時並不會通知父進程 ? SA_ONESHOT/SA_RESETHAND:當調用新的信號處理函數前,將此信號處理方式改成系統預設的方式 ? SA_RESTART:被信號中斷的系統調用會自行重啓 ? SA_NOMASK/SA_NODEFER:在處理此信號未結束前不理會此信號的再次到來 ? SA_SIGINFO:信號處理函數是帶有三個參數的sa_sigaction |
|
oldact |
若是參數oldact不是NULL指針,則原來的信號處理方式會由此結構sigaction返回 |
|
函數返回值 |
成功:0 |
|
出錯:-1,錯誤緣由存於error中 |
||
附加說明 |
信號處理安裝的新舊兩種機制: ① 使用舊的處理機制:struct sigaction act; act.sa_handler=handler_old; ② 使用新的處理機制:struct sigaction act; act.sa_sigaction=handler_new; 並設置sa_flags的SA_SIGINFO位 |
|
錯誤代碼 |
EINVAL:參數signum不合法,或是企圖攔截SIGKILL/SIGSTOP信號 EFAULT:參數act,oldact指針地址沒法存取 EINTR:此調用被中斷 |
(2)sigaction函數使用實例
sigaction.c源代碼以下:
#include
#include
#include
#include
#include
void new_op(int, siginfo_t *, void *);
int main(int argc,char**argv)
{
struct sigaction act;
int sig;
sig=atoi(argv[1]);
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=new_op;
if(sigaction(sig,&act,NULL) < 0)
{
perror("install sigal error");
return -1 ;
}
while(1)
{
sleep(2);
printf("wait for the signal\n");
}
return 0 ;
}
void new_op(int signum,siginfo_t *info,void *myact)
{
printf("receive signal %d\n", signum);
sleep(5);
}
編譯 gcc sigaction.c -o sigaction。
執行 ./sigaction 2,執行結果以下:
wait for the signal
receive signal 2 /*按下Ctrl+C */
退出 /*按下Ctrl-\ */
由 於有時須要把多個信號看成一個集合進行處理,這樣信號集就產生了,信號集用來描述一類信號的集合,Linux所支持的信號能夠所有或部分的出如今信號集 中。信號集操做函數最經常使用的地方就是用於信號屏蔽。好比有時候但願某個進程正確執行,而不想進程受到一些信號的影響,此時就須要用到信號集操做函數完成對 這些信號的屏蔽。
信號集操做函數按照功能和使用順序分爲三類,分別爲建立信號集函數,設置信號屏蔽位函數和查詢被擱置(未決)的信號函數。建立信號集函數只是建立一個信號 的集合,設置信號屏蔽位函數對指定信號集中的信號進行屏蔽,查詢被擱置的信號函數是用來查詢當前「未決」的信號集。信號集函數組並不能完成信號的安裝登記 工做,信號的安裝登記須要經過sigaction函數或signal函數來完成。
查 詢被擱置的信號是信號處理的後續步驟,但不是必需的。因爲有時進程在某時間段內要求阻塞一些信號,程序完成特定工做後解除對該信號阻塞,這個時間段內被阻 塞的信號稱爲「未決」信號。這些信號已經產生,但沒有被處理,sigpending函數用來檢測進程的這些「未決」信號,並進一步決定對它們作何種處理 (包括不處理)。
(1) 建立信號集函數
建立信號集函數有以下5個:
① sigemptyset:初始化信號集合爲空。
② sigfillset:把全部信號加入到集合中,信號集中將包含Linux支持的64種信號。
③ sigaddset:將指定信號加入到信號集合中去。
④ sigdelset:將指定信號從信號集中刪去。
⑤ sigismember:查詢指定信號是否在信號集合之中。
建立信號集合函數原型 |
|
所需頭文件 |
#include |
函數原型 |
int sigemptyset(sigset_t *set) |
int sigfillset(sigset_t *set) |
|
int sigaddset(sigset_t *set,int signum) |
|
int sigdelset(sigset_t *set,int signum) |
|
int sigismember(sigset_t *set,int signum) |
|
函數傳入值 |
set:信號集 |
signum:指定信號值 |
|
函數返回值 |
成功:0(sigismember函數例外,成功返回1,失敗返回 0) |
出錯:-1,錯誤緣由存於error中 |
(2) 設置信號屏蔽位函數
每一個進程都有一個用來描述哪些信號遞送到進程時將被阻塞的信號集,該信號集中的全部信號在遞送到進程後都將被阻塞。調用函數sigprocmask可設定信號集內的信號阻塞或不阻塞。其函數原型及說明以下:
sigprocmask(設置信號屏蔽位) |
所需頭文件 |
#include |
|
函數原型 |
int sigprocmask(int how,const sigset_t *set,sigset_t *oset) |
|
函數傳入值 |
how(決定函數的操做方式) |
SIG_BLOCK:增長一個信號集合到當前進程的阻塞集合之中 |
SIG_UNBLOCK:從當前的阻塞集合之中刪除一個信號集合 |
||
SIG_SETMASK:將當前的信號集合設置爲信號阻塞集合 |
||
set:指定信號集 |
||
oset:信號屏蔽字 |
||
函數返回值 |
成功:0 |
|
出錯:-1,錯誤緣由存於error中 |
(3) 查詢被擱置(未決)信號函數
sigpending函數用來查詢「未決」信號。其函數原型及說明以下:
sigpending(查詢未決信號) |
所需頭文件 |
#include |
函數說明 |
將被擱置的信號集合由參數set指針返回 |
函數原型 |
int sigpending(sigset_t *set) |
函數傳入值 |
set:要檢測信號集 |
函數返回值 |
成功:0 |
出錯:-1,錯誤緣由存於error中 |
|
錯誤代碼 |
EFAULT:參數set指針地址沒法存取 EINTR:此調用被中斷 |
(4) 對信號集操做函數的使用方法
對信號集操做函數的使用方法和順序以下:
① 使用signal或sigaction函數安裝和登記信號的處理。
② 使用sigemptyset等定義信號集函數完成對信號集的定義。
③ 使用sigprocmask函數設置信號屏蔽位。
④ 使用sigpending函數檢測未決信號,非必需步驟。
(5) 信號集操做函數使用實例
該 實例首先使用sigaction函數對SIGINT信號進行安裝登記,安裝登記使用了新舊兩種機制,其中#if 0進行註釋掉的部分爲信號安裝的新機制。接着程序把SIGQUIT、SIGINT兩個信號加入信號集,並把該信號集設爲阻塞狀態。程序開始睡眠30秒,此 時用戶按下Ctrl+C,程序將測試到此未決信號(SIGINT);隨後程序再睡眠30秒後對SIGINT信號解除阻塞,此時將處理SIGINT登記的信 號函數my_func。最後能夠用SIGQUIT(Ctrl+\)信號結束進程執行。
sigset.c源代碼以下:
#include
#include
#include
#include
#include
/*自定義的信號處理函數*/
#if 0
void my_funcnew(int signum, siginfo_t *info,void *myact)
#endif
void my_func(int signum)
{
printf("If you want to quit,please try SIGQUIT\n");
}
int main()
{
sigset_t set, pendset;
struct sigaction action1,action2;
/*設置信號處理方式*/
sigemptyset(&action1.sa_mask);
#if 0 /*信號新的安裝機制*/
action1.sa_flags= SA_SIGINFO;
action1.sa_sigaction=my_funcnew;
#endif
/*信號舊的安裝機制*/
action1.sa_flags= 0;
action1.sa_handler=my_func;
sigaction(SIGINT,&action1,NULL);
/*初始化信號集爲空*/
if(sigemptyset(&set)<0)
{
perror("sigemptyset");
return -1 ;
}
/*將相應的信號加入信號集*/
if(sigaddset(&set,SIGQUIT)<0)
{
perror("sigaddset");
return -1 ;
}
if(sigaddset(&set,SIGINT)<0)
{
perror("sigaddset");
return -1 ;
}
/*設置信號集屏蔽字*/
if(sigprocmask(SIG_BLOCK,&set,NULL)<0)
{
perror("sigprocmask");
return -1 ;
}
else
{
printf("blocked\n");
}
/*測試信號是否加入該信號集*/
if(sigismember(&set,SIGINT)){
printf("SIGINT in set\n") ;
}
sleep( 30 ) ;
/*測試未決信號*/
if ( sigpending(&pendset) <0 )
{
perror("get pending mask error");
}
if(sigismember(&pendset, SIGINT) )
{
printf("signal SIGINT is pending\n");
}
sleep(30) ;
if(sigprocmask(SIG_UNBLOCK,&set,NULL)<0)
{
perror("sigprocmask");
return -1 ;
}
else
printf("unblock\n");
while(1)
{
sleep(1) ;
}
return 0 ;
}
編譯 gcc sigset.c -o sigset。
執行 ./sigset,執行結果以下:
blocked
SIGINT in set /*按下Ctrl+C */
signal SIGINT is pending
If you want to quit,please try SIGQUIT /*按下Ctrl+C */
退出
摘錄自《深刻淺出Linux工具與編程》