Linux系統編程—信號捕捉

前面咱們學習了信號產生的幾種方式,而對於信號的處理有以下幾種方式:程序員

  1. 默認處理方式;
  2. 忽略;
  3. 捕捉。

信號的捕捉,說白了就是抓到一個信號後,執行咱們指定的函數,或者執行咱們指定的動做。下面詳細介紹兩個信號捕捉操做參數:signalsigaction面試

signal函數

函數原型:架構

sighandler_t signal(int signum, sighandler_t handler);函數

其中,sighandler定義是這樣的:typedef void (*sighandler_t)(int);學習

函數做用:網站

註冊一個信號捕捉函數,也就是說,收到了某個信號,就執行它所註冊的回調函數。人工智能

函數參數:spa

signum:信號編號,儘可能用宏來寫,而別用數字,這樣更適合跨平臺;rest

handler:註冊的回調函數;code

函數缺陷:

因爲歷史緣由,該函數在不一樣版本的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:臨時阻塞信號集(或信號屏蔽字)先來看這樣一個情景:

某個信號已經註冊了回調函數,當內核傳遞這個信號過來時,會先通過一個阻塞信號集,先阻塞掉部分信號。再去執行對應的回調函數。以下圖示:

img

假如說,這個回調函數回調執行的時間比較長,好比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,人工智能,等等。公衆號內回覆進羣,邀請您進高手如雲技術交流羣。

img


最後,最近不少小夥伴找我要Linux學習路線圖,因而我根據本身的經驗,利用業餘時間熬夜肝了一個月,整理了一份電子書。不管你是面試仍是自我提高,相信都會對你有幫助!

免費送給你們,只求你們金指給我點個贊!

電子書 | Linux開發學習路線圖

也但願有小夥伴能加入我,把這份電子書作得更完美!

有收穫?但願老鐵們來個三連擊,給更多的人看到這篇文章

推薦閱讀:

相關文章
相關標籤/搜索