信号是與一定的程序相聯系的。也就是說一個程序可以決定在程序中對哪些信号進行什
麼樣的處理。例如一個程序可以忽略某些信号而隻處理其他一些信号另外一個程序還可以選擇如何處理信号。總之這些總與特定的程序相聯系的。是以首
先要建立其信号和程序的對應關系這就是信号的安裝登記。
Linux
主要有兩個函數實作信号的安裝登記signal和sigaction。其中signal在系統調用的基礎上實作是庫函數。它隻有兩個參數不支援信号
傳遞資訊主要是用于前32個非實時信号的安裝而sigaction是較新的函數由兩個系統調用實作sys_signal以及
sys_rt_sigaction有三個參數支援信号傳遞資訊主要用來與sigqueue系統調用配合使用。當然sigaction同樣支援非
實時信号的安裝sigaction優于signal主要展現在支援信号帶有參數。
對
于應用程式自行處理的信号來說信号的生命周期要經過信号的安裝登記、信号集操作、信号的發送和信号的處理四個階段。信号的安裝登記指的是在應用程式中
安裝對此信号的處理方法。信号集操作的作用是用于對指定的一個或多個信号進行信号屏蔽此階段對有些應用程式來說并不需要。信号的發送指的是發送信号可
以通過硬體如在終端上按下Ctrl-C發送的信号和軟體如通過kill函數發送的信号。信号的處理指的是作業系統對接收信号程序的處理處理方法
是先檢查信号集操作函數是否對此信号進行屏蔽如果沒有屏蔽作業系統将按信号安裝函數中登記注冊的處理函數完成對此程序的處理。
1. signal函數
1函數說明
在signal函數中有兩個形參分别代表需要處理的信号編号值和處理信号函數的指針。它主要是用于前32種非實時信号的處理不支援信号的傳遞資訊。但是由于使用簡單易于了解是以在許多場合被程式員使用。
對
于Unix系統來說使用signal函數時自定義處理信号函數執行一次後失效對該信号的處理回到預設處理方式。下面以一個例子進行說明例如一程式
中使用signal(SIGQUIT,
my_func)函數調用其中my_func是自定義函數。應用程序收到SIGQUIT信号時會跳轉到自定義處理信号函數my_func處執行執行
後信号注冊函數my_func失效對SIGQUIT信号的處理回到作業系統的預設處理方式當應用程序再次收到SIGQUIT信号時會按作業系統預設
的處理方式進行處理即不再執行my_func處理函數。而在Linux系統中signal函數已被改寫由sigaction函數封裝實作則不存
在上述問題。
2signal函數原型signal設定信号處理方式
所需頭檔案#include
函數說明設定信号處理方式。signal()會依參數signum指定的信号編号來設定該信号的處理函數。當指定的信号到達時就會跳轉到參數handler指定的函數執行
函數原型void (*signal(int signum,void(* handler)(int)))(int)
函數傳入值signum指定信号編号
handleSIG_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
I have get SIGQUIT
2. sigaction函數
1sigaction函數原型
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參數actoldact指針位址無法存取
EINTR此調用被中斷
2sigaction函數使用執行個體
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
退出
3. 信号集操作函數
由
于有時需要把多個信号當作一個集合進行處理這樣信号集就産生了信号集用來描述一類信号的集合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指定信号值
函數傳回值成功0sigismember函數例外成功傳回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。最後可以用SIGQUITCtrl+\信号結束程序執行。
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
signal SIGINT is pending
If you want to quit,please try SIGQUIT
退出