一、首先,用 kill -l 命令可以查看系統定義的信號列表:
每個信號都有一個編號和一個宏定義名稱,這些宏定義可以在 signal.h 中查看,例如:#define SIGINT 2 。編號34以上的信號爲實時信號。
二、信號常見的三種處理方式:
(1)忽略此信號。(SIG_IGN)
(2)執行信號的默認處理動作。(SIG_DFL)
(3)提供一個信號處理函數,捕捉信號進行自定義處理。
三、信號的產生:
1、常見的幾種產生信號方式:
a.用戶產生信號
b.系統異常產生信號
c.自定義捕捉
2、通過終端按鍵產生信號:
SIGINT的默認處理動作是終止進程,SIGQUIT的默認處理動作是終止進程,並且Core Dump
Core Dump (核心轉儲):當一個進程異常終止時,可以選擇把進程的用戶空間內存數據全部保存到磁盤上,文件名通常是Core,這叫做 Core Dump。
系統默認情況不予 Core 分配空間:
可使用命令 ulimit -c 1024 給Core 分配 1024個字節空間:
實例:寫一個死循環
[lize-h@localhost 0409_Siganl]$ cat core_Dump.c
#include<stdio.h>
int main()
{
printf("pid is %d\n",getpid());
while(1);
return 0;
}
3、調用系統函數向進程發信號:
(1)我們可以用 kill 命令向進程發信號:
[lize-h@localhost 0409_Siganl]$ ./a.out &
[1] 3102
[lize-h@localhost 0409_Siganl]$ kill -SIGSEGV 3102
[lize-h@localhost 0409_Siganl]$
[1]+ 段錯誤 (core dumped) ./a.out
[lize-h@localhost 0409_Siganl]$ cat core_Dump.c
#include<stdio.h>
int main()
{
//printf("pid is %d\n",getpid());
while(1);
return 0;
}
令程序在後臺執行,用 kill 命令向進程發送信號,令進程異常終止,異常信息存入core文件。
(2)發送信號的函數有:
#include<signal.h>
int kill(pid_t pid,int signo); //向任意進程發送任意信號;成功返回0,出錯返回-1。
int raise(int signo); //向自己發送任意信號;成功返回0,出錯返回-1。
#include<stdlib.h>
void abort(void); //向自己發送 abort 信號
(3)alarm函數:
#include<unistd.h>
unsigned int alarm( unsigned int seconds);
/*
調用alarm函數可設置一個鬧鐘;當參數seconds設置爲0表示取消鬧鐘,返回值爲以前設置的
時間剩餘的秒數;當參數seconds設置爲8表示8秒後觸發鬧鐘,向當前進程發送SIGALRM信號,
該信號的默認處理動作是終止當前進程。
*/
四、阻塞信號
1、信號的相關概念:
(1)實際執行信號的處理動作稱爲信號遞達。
(2)信號從產生到遞達之間的狀態,稱爲信號未決(pending)。
(3)進程可以選擇阻塞(Block)某個信號。
(4)被阻塞的信號產生時將保持在未決狀態,直到信號解除對此信號的阻塞,才執行遞達的動作。
(5)阻塞和忽略不同,只要信號被阻塞就不會遞達,而忽略是在遞達後可選的一種處理動作。
(6)發送信號相當於更改PCB中的位圖。
2、在內核中的表示:
Linux信號的實現:常規信號在遞達之前產生多次只計一次,而實時信號在遞達之前產生多次可以依次放在一個隊列裏。
3、sigset_t 信號集:
未決和阻塞標誌可以用相同的數據類型sigset_t來存儲,sigset_t稱爲信號集,這個類型可以表示每個信號的“有效”或“無效”狀態,在阻塞信號集中“有效”和“無效”的含義是該信號是否被阻塞,而在未決信號集中“有效”和“無效”的含義是該信號是否處於未決狀態。阻塞信號集也叫做當前進程的信號屏蔽字(Signal Mask),這裏的“屏蔽”應該理解爲阻塞而不是忽略。
4、信號集操作函數:
#include<signal.h>
int sigemptyset(sigset_t* set); //初始化set所指向的信號集,使其中所有信號的對應bit清零。
int sigfillset(sigset_t* set); //初始化set所指向的信號集,使其中所有信號的對應bit置1。
int sigaddset(sigset_t* set,int signo); //在信號集中添加編號爲signo的信號
int sigdelset(sigset_t* set,int signo); //刪除信號集中編號爲signo的信號
//以上四個函數成功返回0,出錯返回-1。
int sigismember(const sigset_t* set,int signo);
//sigismember是一個布爾函數,用於判斷一個信號集的有效信號中是否包含某種信號,若包含則返回1,不包含返回0,出錯返回-1。
sigprocmask函數:
調用該函數可以讀取或更改進程的信號屏蔽字(阻塞信號集)
#include<signal.h>
int sigprocmask(int how,const sigset_t* set,sigset_t* oset);
//若調用成功返回0,出錯返回-1
如果oset爲空指針,則讀取進程的當前信號屏蔽字通過oset參數傳出。如果set是非空指針,則更改進程的信號屏蔽字,參數how指示如何更改。如果oset和set都是非空指針,則先將原來的信號屏蔽字備份到oset裏,然後根據set和how參數更改信號屏蔽字。假設當前的信號屏蔽字爲mask,以下爲how參數的可選值。
sigpending函數:
#include<signal.h>
int sigpending(sigset_t* set)
//讀取當前進程的未決信號集,通過set參數傳出。調用成功返回0,出錯返回-1。
範例:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void printsigset(sigset_t *set)
{
int i=0;
for(;i<32;i++)
{
if(sigismember(set,i))
{
putchar('1');
}
else
{
putchar('0');
}
}
puts("");
}
int main()
{
sigset_t s,p;
sigemptyset(&s);
sigaddset(&s,SIGINT);
gprocmask(SIG_BLOCK,&s,NULL);
while(1)
{
sigpending(&p);
printsigset(&p);
sleep(1);
}
return 0;
}
運行結果:
[lize-h@localhost 0409_Siganl]$ ./a.out
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
^C10100000000000000000000000000000
10100000000000000000000000000000
10100000000000000000000000000000
10100000000000000000000000000000
^Z
[1]+ Stopped ./a.out
[lize-h@localhost 0409_Siganl]$