本文關鍵字: 信號處理函數,signal(),信號集函數組
信號處理的方法主要有兩種,一種是使用signal()函數,另一種是使用信號集函數組。下面分別介紹這兩種處理方式。
1)使用signal()函數
使用signal()函數處理時,只需指出要處理的信號和處理函數即可。它主要用于前32種非實時信號的處理,不支持信號傳遞信息,但是由于使用簡單、易于理解,因此也受到很多程序員的歡迎。Linux還支持一個更健壯更新的信號處理函數sigaction(),推薦使用該函數。
signal()函數的語法要點如表1所示。
表1 signal()函數語法要點
所需頭文件 |
#include <signal.h> |
函數原型 |
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
|
函數傳入值 |
signum:指定信號代碼 |
handler |
SIG_IGN:忽略該信號 |
SIG_DFL:采用系統默認方式處理信號 |
自定義的信號處理函數指針 |
函數返回值 |
成功:以前的信號處理配置 |
出錯:-1 |
這里需要對該函數原型進行說明。這個函數原型有點復雜:首先該函數原型整體指向一個無返回值并且帶一個整型參數的函數指針,也就是信號的原始配置函數;接著該原型又帶有兩個參數,其中第2個參數可以是用戶自定義的信號處理函數的函數指針。
表2列舉了sigaction()函數的語法要點。
表2 sigaction()函數語法要點
所需頭文件 |
#include <signal.h> |
函數原型 |
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) |
函數傳入值 |
signum:信號代碼,可以為除SIGKILL及SIGSTOP外的任何一個特定有效的信號 |
act:指向結構sigaction的一個實例的指針,指定對特定信號的處理 |
oldact:保存原來對相應信號的處理 |
函數返回值 |
成功:0 |
出錯:-1 |
這里要說明的是sigaction()函數中第2和第3個參數用到的sigaction結構,這是一個看似非常復雜的結構,希望讀者能夠慢慢閱讀此段內容。
首先給出了sigaction的定義,代碼如下:
struct sigaction
{
void (*sa_handler)(int signo);
sigset_t sa_mask;
int sa_flags;
void (*sa_restore)(void);
}
sa_handler是一個函數指針,指定信號處理函數,這里除可以是用戶自定義的處理函數外,還可以為SIG_DFL(采用默認的處理方式)或SIG_IGN(忽略信號)。它的處理函數只有一個參數,即信號值。
sa_mask是一個信號集,它可以指定在信號處理程序執行過程中哪些信號應當被屏蔽,在調用信號捕獲函數前,該信號集要加入到信號的信號屏蔽字中。
sa_flags中包含了許多標志位,是對信號進行處理的各個選擇項。它的常見可選值如表3所示。
表3 常見信號的含義及其默認操作
信 號 |
含 義 |
SA_NODEFER / SA_NOMASK |
當捕捉到此信號時,在執行其信號捕捉函數時,系統不會自動屏蔽此信號 |
SA_NOCLDSTOP |
進程忽略子進程產生的任何SIGSTOP、SIGTSTP、SIGTTIN和SIGTTOU信號 |
SA_RESTART |
令重啟的系統調用起作用 |
SA_ONESHOT / SA_RESETHAND |
自定義信號只執行一次,在執行完畢后恢復信號的系統默認動作 |
以下實例表明了如何使用signal()函數捕捉相應信號,并做出給定的處理。這里,my_func就是信號處理的函數指針,讀者還可以將其改為SIG_IGN或SIG_DFL查看運行結果。第2個實例是用sigaction()函數實現同樣的功能。
以下是使用signal()函數的示例:
/* signal.c */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
/* 自定義信號處理函數 */
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();
exit(0);
}
運行結果如下:
$ ./signal
Waiting for signal SIGINT or SIGQUIT...
I have get SIGINT (按Ctrl+c 組合鍵)
$ ./signal
Waiting for signal SIGINT or SIGQUIT...
I have get SIGQUIT (按Ctrl+\ 組合鍵)
以下是用sigaction()函數實現同樣的功能,只列出了更新的main()函數部分。
/* sigaction.c */
/* 前部分省略 */
int main()
{
struct sigaction action;
printf("Waiting for signal SIGINT or SIGQUIT...\n");
/* sigaction結構初始化 */
action.sa_handler = my_func;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
/* 發出相應的信號,并跳轉到信號處理函數處 */
sigaction(SIGINT, &action, 0);
sigaction(SIGQUIT, &action, 0);
pause();
exit(0);
}
2)信號集函數組
使用信號集函數組處理信號時涉及一系列的函數,這些函數按照調用的先后次序可分為以下幾大功能模塊:創建信號集、注冊信號處理函數及檢測信號。
其中,創建信號集主要用于處理用戶感興趣的一些信號,其函數包括以下幾個。
● sigemptyset():將信號集初始化為空。
● sigfillset():將信號集初始化為包含所有已定義的信號集。
● sigaddset():將指定信號加入到信號集中。
● sigdelset():將指定信號從信號集中刪除。
● sigismember():查詢指定信號是否在信號集中。
注冊信號處理函數主要用于決定進程如何處理信號。這里要注意的是,信號集里的信號并不是真正可以處理的信號,只有當信號的狀態處于非阻塞狀態時才會真正起作用。因此,首先使用sigprocmask()函數檢測并更改信號屏蔽字(信號屏蔽字是用來指定當前被阻塞的一組信號,它們不會被進程接收),然后使用sigaction()函數來定義進程接收到特定信號后的行為。
檢測信號是信號處理的后續步驟,因為被阻塞的信號不會傳遞給進程,所以這些信號就處于“未處理”狀態(也就是進程不清楚它的存在)。sigpending()函數允許進程檢測“未處理”信號,并進一步決定對它們做何處理。
首先介紹創建信號集的函數格式,表4列舉了這一組函數的語法要點。
表4 創建信號集函數語法要點
所需頭文件 |
#include <signal.h> |
函數原型 |
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 |
表5列舉了sigprocmask()函數的語法要點。
表5 sigprocmask()函數語法要點
所需頭文件 |
#include <signal.h> |
函數原型 |
int sigprocmask(int how, const sigset_t *set, sigset_t *oset) |
函數傳入值 |
how:決定函數的操作方式 |
SIG_BLOCK:增加一個信號集到當前進程的阻塞集中 |
SIG_UNBLOCK:從當前的阻塞集中刪除一個信號集 |
SIG_SETMASK:將當前的信號集設置為信號阻塞集 |
set:指定信號集 |
oset:信號屏蔽字 |
函數返回值 |
成功:0 |
出錯:-1 |
此處,若set是一個非空指針,則參數how表示函數的操作方式;若how為空,則表示忽略此操作。
表6列舉了sigpending()函數的語法要點。
表6 sigpending()函數語法要點
所需頭文件 |
#include <signal.h> |
函數原型 |
int sigpending(sigset_t *set) |
函數傳入值 |
set:要檢測的信號集 |
函數返回值 |
成功:0 |
出錯:-1 |
總之,在處理信號時,一般遵循如圖1所示的操作流程。
 圖1 一般的信號操作處理流程
本文選自華清遠見嵌入式培訓教材《從實踐中學嵌入式Linux應用程序開發》
熱點鏈接:
1、信號捕捉函數alarm()和pause()
2、信號發送函數kill()和raise()
3、Linux下的信號機制
4、有名管道(FIFO)
5、標準流管道
更多新聞>> |