本文承接上文Liunx系統程式設計篇—程序通信(五)信号(一)(原理、概述、建立、實戰)(kill、signal)上文對于信号的一些理論,本文就不再贅述。
本文重點是講解:sigaction、sigqueue兩個函數,分别用于信号處理函數的注冊、信号處理發送函數是signal和kill的更新版。
常用API
信号處理函數的注冊
入門版:函數signal
進階版:函數sigaction
信号處理發送函數
1.入門版:kill
2.進階版:sigqueue
對于入門版的信号處理API的重點在于動作,但kill 函數發送的信号是無法攜帶資料。
對于進階版的信号處理API的==重點信号攜帶的資訊
sigaction函數
sigaction 是一個系統調用,可以用來查詢或設定信号處理方式。
包含的頭檔案
函數原型:
signum:參數指出要捕獲的信号類型。
act:參數指定新的信号處理方式,struct sigaction類型如果不為空說明需要對該信号有新的配置。
oldact:備份,如果不為空,那麼可以對之前的信号配置進行備份,以友善之後進行恢複。
struct sigaction結構體介紹
struct sigaction {
void (*sa_handler)(int); //信号處理程式,不接受額外資料,SIG_IGN 為忽略,SIG_DFL 為預設動作
void (*sa_sigaction)(int, siginfo_t *, void *); //信号處理程式,能夠接受額外資料和sigqueue配合使用
sigset_t sa_mask;//阻塞關鍵字的信号集,可以再調用捕捉函數之前,把信号添加到信号阻塞字,信号捕捉函數傳回之前恢複為原先的值。
int sa_flags;//影響信号的行為SA_SIGINFO表示能夠接受資料
};
//回調函數句柄sa_handler、sa_sigaction隻能任選其一
sa_handler
sa_handler此參數和signal()的參數handler相同,代表新的信号處理函數
void (*sa_sigaction)(int, siginfo_t *, void *);
關于
void (*sa_sigaction)(int, siginfo_t *, void *);
處理函數來說還需要有一些說明。
void* 是接收到信号所攜帶的額外資料;
而struct siginfo這個結構體主要适用于記錄接收信号的一些相關資訊。
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count; POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
int si_band; /* Band event */
int si_fd; /* File descriptor */
}
其中的成員很多,si_signo 和 si_code 是必須實作的兩個成員。可以通過這個結構體擷取到信号的相關資訊。
關于發送過來的資料是存在兩個地方的,sigval_t si_value這個成員中有儲存了發送過來的資訊;
同時,在si_int或者si_ptr成員中也儲存了對應的資料。
sa_mask
sa_mask 用來設定在處理該信号時暫時将sa_mask 指定的信号集擱置
sa_mask 成員,設定在其的信号集中的信号,會在捕捉函數調用前設定為阻塞,并在捕捉函數傳回時恢複預設原有設定。這樣的目的是,在調用信号處理函數時,就可以阻塞默寫信号了。在信号處理函數被調用時,作業系統會建立新的信号阻塞字,包括正在被遞送的信号。是以,可以保證在處理一個給定信号時,如果這個種信号再次發生,那麼他會被阻塞到對之前一個信号的處理結束為止。
sa_flags
sa_flags 用來設定信号處理的其他相關操作,下列的數值可用:
SA_RESETHAND:當調用信号處理函數時,将信号的處理函數重置為預設值SIG_DFL
SA_RESTART:如果信号中斷了程序的某個系統調用,則系統自動啟動該系統調用
SA_NODEFER :一般情況下, 當信号處理函數運作時,核心将阻塞該給定信号。但是如果設定了 SA_NODEFER标記, 那麼在該信号處理函數運作時,核心将不會阻塞該信号
sigqueue函數
在隊列中向指定程序發送一個信号和資料
包含的頭檔案
函數原型:
union sigval {
int sival_int;
void *sival_ptr;
};
pid:發給誰,是目标程序的程序号
sig:發的是什麼信号,是信号代号
value:發送的消息(int或者char*),是一個聯合體,表示信号附帶的資料,附帶資料可以是一個整數也可以是一個指針,有如下形式:
union sigval {
int sival_int;
void *sival_ptr;//指向要傳遞的信号參數
};value
使用這個函數之前,必須要有幾個操作需要完成
1、使用 sigaction 函數安裝信号處理程式時,制定了 SA_SIGINFO 的标志=。
2、sigaction 結構體中的sa_sigaction成員提供了信号捕捉函數。如果實作的sa_handler成員,那麼将無法擷取額外攜帶的資料。
3、sigqueue 函數隻能把信号發送給單個程序,可以使用 value 參數向信号處理程式傳遞整數值或者指針值。
4、、sigqueue 函數不但可以發送額外的資料,還可以讓信号進行排隊(作業系統必須實作了 POSIX.1的實時擴充),對于設定了阻塞的信号,使用 sigqueue 發送多個同一信号,在解除阻塞時,接受者會接收到發送的信号隊列中的信号,而不是直接收到一次。但是,信号不能無限的排隊,信号排隊的最大值受到SIGQUEUE_MAX的限制,達到最大限制後,sigqueue 會失敗,errno 會被設定為 EAGAIN。
執行個體:
demo.c
#include<stdio.h>
#include <signal.h>
/*信号處理函數*/
void handler(int signum, siginfo_t *info, void *context)
{
printf("get signum %d\n",signum);
if(context!=NULL){//如果有内容
printf("get data=%d\n",info->si_int);
printf("get data=%d\n",info->si_value.sival_int);
}
}
int main()
{
struct sigaction act;
act.sa_sigaction = handler;//信号處理程式,能夠接受額外資料和sigqueue配合使用
act.sa_flags = SA_SIGINFO;//影響信号的行為SA_SIGINFO表示能夠接受資料
sigaction(SIGUSR1,&act,NULL);
sigaction(SIGINT,&act,NULL);
while(1);
return 0;
}
send.c
#include <signal.h>
#include <stdio.h>
int main(int argc,char **argv)
{
int pid = atoi(argv[2]);
int signum = atoi(argv[1]);
union sigval value;
value.sival_int = 100;
sigqueue(pid,signum,value);//信号處理發送函數
printf("done\n");
return 0;
}
實驗結果:
