如何驗證Linux Signal可靠信號與不可靠信號

前面章節已經<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),回覆:領取學習資料。或者回復:進入技術交流羣。網盤資料有如下:

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章