前面章节已经<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),回复:领取学习资料。或者回复:进入技术交流群。网盘资料有如下: