如何验证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),回复:领取学习资料。或者回复:进入技术交流群。网盘资料有如下:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值