细说linux信号 一

l         什么是信号

信号是一个消息,用来进行进程间的相互通信,这和人们用e_mail相互通信类似。所以信号的处理一定是在某一进程中进行的。

信号的发生是由于某一事件而引起的,这些事件可以是:1、由硬件产生的硬件异常(比如除零操作),2、由软件产生信号(比如先前设置的闹钟时间到时),3、从终端产生的信号,4、使用kill函数。

Linux下有三十多种的信号,不同的信号其默认的处理是不一样的,对于一些无关紧要的信号,进程在接收后不做任何处理(比如:当子进程的状态发生改变时向父进程发送的SIGCHLD信号),另外一些信号可能导致进程停止当前的操作转而执行其它的操作(比如:当你按下CTRLC时,SIGINT信号被送往当前进程,进程在接收到该信号后立刻终止运行)。

很多时候我们并希望使用系统默认的信号方式(比如:在一款游戏软件中,我们希望当进程接收到SIGINT信号后并不直接退出,而是先保存当前的数据然后在退出,这样当用户下次执行游戏时就能从上次被停止的地方继续执行),linux提供了许多和信号处理相关的函数,这些函数使得进程对信号的处理变得相当的灵活。

l         最简单的信号函数

首先解释下什么叫做信号句柄(signal handler):函数和变量一样都是有地址的,我们说函数的地址是指该函数在内存中的起始位置,编译器把这个地址告诉cpu,以便cpu找到该函数并执行。所谓信号句柄就是当进程接收到某一信号后执行那个函数的地址。它告诉进程当这个信号到来是应该运行那个函数。

void (*signal (int signo, void (*func)(int))) (int )

如果该函数调用成功将返回前次设置的信号句柄,否则返回SIG_ERR

signal函数设置当进程接收到某一信号(singno)时所执行动作。func 可以是a) 常量SIG_IGN(忽略该信号), b) 常量SIG_DEF(使用系统默认的处理方法),c)一个函数的地址(当信号到达时系统将转而执行该函数)。

当进程调用exec家族的函数后,所有信号都将被置为SIG_DEL或者SIG_IGN(这是由于exec改变了进程中代码段和数据段中的内容,所以原先设置的信号句柄在这时可能有其它的意义了)。

当进程调用fork函数产生子进程时,原先对信号的设置将被子进程所继承(因为子进程原样复制了父亲进程的代码段和数据段中的数据,或者它们共享相同的地址空间(COW),这取决与操作系统的实现)。

此外从<signal.h>头文件我们可以看到如下定义:

#define SIG_ERR (void (*) ())-1

#define SIG_DEL (void (*) ()) 0

#define SIG_IGN (void (*) ()) 1

这些宏告诉我们如何将一个常量转换为函数指针

l         不可靠的信号(unreliable signal

早期的操作系统对信号的处理存在着一个问题:当某信号发生是,系统将还原对该信号的设置。于是有人写下面这段代码:

int sig_int()/* my signal function*/

main(){

signal(SIG_INT,sig_int);

}

sig_int(int signo){

       signal(SIG_INT,sig_int);

      

}

乍看起来这里是不会有问题的,因为当进程第一次收到SIG_INT并执行sig_int句柄后虽然系统还原了对SIG_INT的默认处理(结束进程),但是由于进程再次调用了signal函数而使得当下个SIG_INT到来时进程递归进入下个sig_int句柄(而不是退出该进程)。

不幸的是这段代码是错误的,原因是忽略了窗口时间(window of time ),所谓窗口时间是指从信号发生到在信号句柄内(sig_int函数)调用signal函数前的这段时间。如果这时恰好进程又收到一个SIG_INT信号,由于信号系统默认的SIG_INT是终止进程,于是该进程将被终止。

                     早期系统存在的另外一个问题是我们无法关闭一个信号(阻止该信号出现), 能做的只有忽略(SIG_IGN)该信号。

l         可重入函数(reentrant function

在信号句柄中我们无法告诉进程该信号到来前一刻程序究竟运行到了哪个位置。假设信号到来前进程正通过malloc分配一个地址空间,并且信号句柄中我们再次调用了malloc,这时会发生什么事呢?由于malloc会修改一个保存空闲地址的链表,而且这个链表是全局数据类型,于是当第一个malloc正在修改这个链表时(修改未完成),由于一个信号到来,进程转而去执行信号句柄,而信号句柄中又调用malloc函数,第二个malloc也修改同一个链表,这样就产生了错误的数据。类似malloc的就是不可重入的函数,其它的就叫做可重入函数。

       判断一个函数是否为一个不可重入函数一帮按照下面标准:1、使用静态数据结构,2、有调用mallocfree函数,3、是标准I/O库的一部分(因为大部分的标准I/O库使用了全局数据结构)。

       通过调用sigsetjmp函数(以后将会说明)可以避免不可重入函数所产生的问题。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值