人们往往将信号称为“软件中断”,它提供了异步事件的处理机制,这些事件可以来自系统外部(如用户按下ctrl+c产生中断符),也可能来自程序或者内核内部的执行动作(如进程除零操作)。进程收到信号,就意味着某一事件或异常情况的发生。
信号的关键不仅在于事件的发生是异步的,而且程序对信号的处理也是异步的。信号处理函数在内核注册,收到信号时,内核从程序的其他部分异步调用信号处理函数。信号的生命周期比较明确:产生信号(也可以说是信号发出或者生成)、内核存储信号、内核发送信号、内核处理信号。
信号的类型很多,每一种分别标识不同的事件或情况。采用不同的整数来标识各种信号类型,
并以 SIGxxxx 形式的符号名加以定义。在头文件<signal.h>中,信号名都被定义为正整数常量(信号编号)。不存在编号为0的信号,Kill函数对信号编号0有特殊的应用(将信号发送给当前进程所属进程组中的所有进程),POSIX.1将此类信号编号称为空信号。
内核、其他进程(只要具有相应的权限)或进程自身均可向进程发送信号。例如,发生下列情况之一时,内核可向进程发送信号:用户键入中断字符(通常为 Control-C)、进程的子进程之一已经终止、由进程设定的定时器(告警时钟)已经到期、进程尝试访问无效的内存地址、在 shell 中,可使用 kill 命令向进程发送信号。在程序内部,系统调用 kill()可提供相同的功能。
收到信号时,进程会根据信号采取如下动作之一。
1、忽略信号:大多数信号都可以按照这种方式进行处理,但是有两个特例,SIGKILL和SIGSTOP信号,这两种信号不能被忽略的原因是:它们向内核和超级用户提供了让进程终止或者停止的可靠方法。
2、捕捉信号:为实现这点,需要通知内核在某种信号发生时,调用一个用户态的函数,在此函数中,可完成用户程序对信号所代表的事件的处理逻辑。注意不能捕捉SIGKILL和SIGSTOP信号。
3、执行系统的默认操作:对每种信号的默认处理逻辑见下图,注意对大多数信号而言,系统的默认动作是终止该进程。
默认动作中,“终止+core”表示进程在当前工作目录的core文件中复制了该进程的内存映像(该文件名为core)。大多数UNIX系统调试程序都是用core文件检查进程终止时的状态。
参考:《Linux系统编程手册》、《Unix环境高级编程第三版》、《Linux系统编程第二版》