前面我們學習了信號產生的幾種方式,而對于信號的處理有如下幾種方式:
- 默認處理方式;
- 忽略;
- 捕捉,
信號的捕捉,說白了就是抓到一個信號后,執行我們指定的函式,或者執行我們指定的動作,下面詳細介紹兩個信號捕捉操作引數:signal和sigaction,
signal函式
函式原型:
sighandler_t signal(int signum, sighandler_t handler);
其中,sighandler定義是這樣的:typedef void (*sighandler_t)(int);
函式作用:
注冊一個信號捕捉函式,也就是說,收到了某個信號,就執行它所注冊的回呼函式,
函式引數:
signum:信號編號,盡量用宏來寫,而別用數字,這樣更適合跨平臺;
handler:注冊的回呼函式;
函式缺陷:
由于歷史原因,該函式在不同版本的Unix和Linux系統中可能起到的效果不一樣,所以跨平臺性不佳,盡量避免使用它,取而代之使用通用性更好的sigaction函式,
#include <stdio.h>
#include <signal.h>
void func()
{
printf("SIGQUIT catched!\n");
}
int main()
{
signal(SIGQUIT, func);
while(1);
}
sigaction函式
函式原型:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
函式作用:
與signal函式類似,用來注冊一個信號捕捉函式;
回傳值:
成功:0;失敗:-1,并設定errno;
引數:
signum:信號編號,盡量用宏來寫,而別用數字,這樣更適合跨平臺;
act:傳入引數,新的信號捕捉方式;
oldact:傳出引數,舊的信號捕捉方式
這里特別要注意引數中struct sigaction結構體,這也是這個函式的難點所在,下面詳細說明:
struct 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_restorer和sa_sigaction這兩個成員一個已經被棄用了,另一個很少使用,所以我們暫且不管它們,重點掌握剩下的三個,
① sa_handler:指定信號捕捉后的處理函式,即注冊回呼函式,該成員也可以賦值為SIG_IGN,表示忽略該信號,也可注冊為SIG_DFL,表示執行信號的默認動作,
② sa_mask:臨時阻塞信號集(或信號屏蔽字)先來看這樣一個情景:
某個信號已經注冊了回呼函式,當內核傳遞這個信號過來時,會先經過一個阻塞信號集,先阻塞掉部分信號,再去執行對應的回呼函式,如下圖示:

假如說,這個回呼函式回呼執行的時間比較長,比如2秒,在這2秒里,又有其它的信號過來,那行程是暫停當前回呼函式,去回應新的信號,還是不管新來的信號,先把當前回呼函式處理完再說?
正確的做法是,在執行回呼函式期間,使用sa_mask臨時的去替代行程的阻塞信號集,保證回呼函式安心的執行完畢,再解除替代,注意:這個程序僅僅發生在回呼函式執行期間,是臨時性的設定,
③ sa_flags:通常設定為0,表示使用默認屬性,
再來看另外一個場景:
比如行程對SIGQUIT注冊了回呼函式,當回呼函式在執行期間,又來了SIGQUIT函式,這時,行程是回應還是不回應該信號?這就是sa_flags的一個作用,當其設定為0時,表示使用默認屬性,也就是先不回應該信號,而是執行完回呼函式再處理此信號,
另外,阻塞的常規信號不支持排隊,也就是說,執行回呼函式期間,再來千百個同個信號時,系統只記錄一次,而后面的32個實時信號則支持排隊,
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void func(int signal)
{
printf("SIGQUIT catched!\n");
sleep(2); //用來模擬回呼函式執行很長時間
printf("func finished!\n");
}
int main()
{
struct sigaction act;
act.sa_handler = func;
sigemptyset(&act.sa_mask); //先清空臨時阻塞信號集
sigaddset(&act.sa_mask, SIGINT); // 執行回呼函式期間,屏蔽SIGINT
act.sa_flags = 0;
sigaction(SIGQUIT, &act, NULL); //注冊回呼函式
while(1);
return 0;
}
更多精彩內容,請關注公眾號良許Linux,公眾內回復1024可免費獲得5T技術資料,包括:Linux,C/C++,Python,樹莓派,嵌入式,Java,人工智能,等等,公眾號內回復進群,邀請您進高手如云技術交流群,
公眾號:良許Linux
有識訓?希望老鐵們來個三連擊,給更多的人看到這篇文章
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/176515.html
標籤:其他
