前面章節已經<Linux下的Signal信號處理及詳解>說了,Linux 下的信號分爲可靠信號和不可靠信號,或稱爲實時信號和非實時信號,信號是從1開始編號的,不存在0號信號。0信號用來測試對應進程是否存在或者是否由權限給其發送信號。
可靠信號是爲了彌補Linux的不可靠信號以及用戶可使用的信號太少的缺陷。怎樣理解可靠與不可靠?下面要引進幾個概述。
Linux 不可靠信號
還是前面將的例子,SIGINT信號,只是證明它的不可靠性
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#define MSG "Catch signal SIGINT processing \n"
#define MSG_END "Finished process SIGINT return \n"
void time_fun() ;
static void sig_handler (int signuum )
{
/*
在信號處理程序中,儘量不要調用與標準IO相關的和不可重入的函數。
STDIN_FILENO:接收鍵盤的輸入
STDOUT_FILENO:向屏幕輸出
*/
write ( STDOUT_FILENO , MSG , strlen (MSG) ) ;
time_fun();
write ( STDOUT_FILENO , MSG_END , strlen (MSG_END) ) ;
}
void time_fun()
{
long long s = 0 ;
long long i ;
for ( i= 0 ; i < 500000000L ; i++ )
{
s += i ;
}
}
int main(int argc, char **argv)
{
// 註冊信號處理函數
if ( SIG_ERR == signal ( SIGINT , sig_handler ) )
{
fprintf (stderr , "signal error ") , perror ("") ;
exit (1) ;
}
// 讓主程序不退出,掛起,等待信號產生
while (1)
{
pause () ; //使調用進程在接到一信號前掛起。
}
return 0 ;
}
編譯輸出:
當我們在執行示例程序的時候,如果在打印完 Catch signal SIGINT processing 之後,我們很快多次按下Ctrl-C。會發現當打印完 Finished process SIGINT return 後,僅會再響應一次信號。這是爲什麼?爲什麼我們按下那麼多次Ctrl-C卻只響應那麼一次。原因其時就在於SIGINT是一個不可靠信號。
[1~31]均爲不可靠信號。證實了SIGINT不是可靠信號,因爲多次發生後會丟失。如何驗證信號被屏蔽後多次發生,再解除屏蔽後只會遞送一次?需要用到sigprocmask函數。再自己寫一個簡單的腳本多次發送信號,就可以了。這裏就不多進行演示了。
Linux 可靠信號
驗證可靠信號,其實就是將第一個程序的註冊信號改成SIGRTMIN,再改改信號處理函數中打印的信息,來看看個例子:
// filenam : simple_realiable_signal.cpp
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#define MSG "Catch signal SIGRTMIN processing \n"
#define MSG_END "Finished process SIGRTMIN return \n"
void do_too_heavy_work () {
long long s = 0 ;
long long i;
for ( i= 0 ; i < 500000000L ; i++ ) {
s += i ;
}
}
void sig_handler (int signuum ) {
// 在信號處理程序中,儘量不要調用與標準IO相關的,不可重入的函數。
write ( STDOUT_FILENO , MSG , strlen (MSG) ) ;
do_too_heavy_work();
write ( STDOUT_FILENO , MSG_END , strlen (MSG_END) ) ;
}
int main(int argc, char **argv) {
// 註冊信號處理函數
if ( SIG_ERR == signal ( SIGRTMIN , sig_handler ) ) {
fprintf (stderr , "signal error ") , perror ("") ;
exit (1) ;
}
// 讓主程序不退出,掛起,等待信號產生
while (1) {
pause () ;
}
return EXIT_SUCCESS ;
}
還有一個腳本,用來發送信號:
#!/bin/bash
#過濾出signal的pid
pid=$(ps aux|grep realiable_signal | grep -v grep | awk '{print $2}')
#循環發送5次信號
for((i=0;i<5;i++))
do
kill -34 $pid
done
執行一下看看,首先編譯signal
再運行腳本:
看看運行結果:
[32~63]爲可靠信號或者叫實時信號。
上面講講本質上的原因,或許現在不能很快理解,想詳細瞭解本質上的原因,大家可以去看內核源碼的東西,內核裏寫的纔是真實-原理的東西。
總結
在信號處理函數中調用一個非可重入函數,其結果未知的,所以儘量不要在信號處理
函數中使用非可重入函數 , 判斷是否是可重入函數有一個簡單的原則:
1、函數內部不使用操作靜態變量。
2、不使用與標準I/O相關函數。
3、不使用malloc ,free() 函數。
4、不調用其它非可重入函數。
關於可重入的概念,大家自行查找一下,或參考APUE相關章節,這裏就不多總結了。
參考:《Unix環境高級編程 第三版》
歡迎關注公衆號【程序猿編碼】,添加本人微信號(17865354792),回覆:領取學習資料。或者回復:進入技術交流羣。網盤資料有如下: