Linux系統程式設計—訊號捕捉

cnnbull發表於2021-09-09

前面我們學習了訊號產生的幾種方式,而對於訊號的處理有如下幾種方式:

  1. 預設處理方式;
  2. 忽略;
  3. 捕捉。

訊號的捕捉,說白了就是抓到一個訊號後,執行我們指定的函式,或者執行我們指定的動作。下面詳細介紹兩個訊號捕捉操作引數:signalsigaction

##signal函式

函式原型:

sighandler_t signal(int signum, sighandler_t handler);

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

函式作用:

註冊一個訊號捕捉函式,也就是說,收到了某個訊號,就執行它所註冊的回撥函式。

函式引數:

signum:訊號編號,儘量用宏來寫,而別用數字,這樣更適合跨平臺;

handler:註冊的回撥函式;

函式缺陷:

由於歷史原因,該函式在不同版本的Unix和Linux系統中可能起到的效果不一樣,所以跨平臺性不佳,儘量避免使用它,取而代之使用通用性更好的sigaction函式。

 #include 
 #include 
 
 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 
 #include 
 #include 
 
 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學習路線圖,於是我根據自己的經驗,利用業餘時間熬夜肝了一個月,整理了一份電子書。無論你是面試還是自我提升,相信都會對你有幫助!目錄如下:

圖片描述

免費送給大家,只求大家金指給我點個贊!

也希望有小夥伴能加入我,把這份電子書做得更完美!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2524/viewspace-2826412/,如需轉載,請註明出處,否則將追究法律責任。

相關文章