信号
信号(软中断信号),用于通知进程发生了异步事件(它是Linux系统响应某些条件而产生的一个事件,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的)。
信号是进程间通信机制的唯一异步通信机制,一个进程不必通过任何操作来等待信号的到达。当进程接收到一个信号时,也会相应地采取一些行动。
生成:一个信号的产生。
捕获:进程接收到一个信号。
在linux系统中,信号可能是由于系统中某些错误而产生(如内存段冲突、浮点处理器错误或非法指令等,由shell和终端处理器生成并且引起中断),也可以是某个进程主动生成的一个信号(可以作为在进程间传递通知或修改行为的一种方式,它可以明确地由一个进程发送给另一个进程,当进程捕获到这个信号就会按照程序进行相应操作并且去处理它)。
无论何种情况,他们的编程接口都是相同的,信号可以被生成、捕获、响应或忽略。
进程间可以互相发送信号,内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。
系统支持的信号
kill -l可查看系统支持的信号类型。
1~31信号值的信号属性为非实时信号(不可靠信号),34~64信号值的信号属性为实时信号(可靠信号),总共62个信号类型。具体信号类型意思可自行百度。
一般而言,信号的响应处理过程为:
如果该信号被阻塞,那么将该信号挂起,不对其做任何处理,等到解除对其阻塞为止。
如果该信号被捕获,那么将进一步判断捕获的类型(如果设置为响应函数,那么执行该响应函数;如果设置为忽略,那么直接丢弃该信号。)最后才执行信号的默认处理。
非实时信号和实时信号
非实时信号,主要是因为这类信号不支持排队,因此信号可能会丢失。比如发送多次相同的信号,进程只能收到一次,也只会处理一次,因此剩下的信号将被丢弃。
实时信号,主要是因为这类信号支持排队,发送了多少个信号给进程,进程就会处理多少次。
一般来说,一个进程收到一个信号后不会被立即处理,而是在恰当时机进行处理(一般是在中断返回时,或常见是内核态返回用户态时)。
即使收到信号,进程也不一定会立即去处理它,因为系统不会为了处理一个信号而把当前正在运行的进程挂起,因为这样子系统的资源消耗过大。如果不是紧急信号,是不会立即处理的,所以系统一般都会选择在内核态切换回用户态时处理信号,比如有时候进程处于休眠状态,但是又收到一个信号,于是系统就得把信号存储在进程唯一的进程PCB中。
信号的处理
生成信号的事件:程序错误、外部事件、显示请求。
程序错误:零作除数、非法存储访问等,这种情况通常是由硬件而不是由Linux内核检测到的,但由内核向发生此错误的那个进程发送相应的信号。
外部事件:当用户在终端按下某些键时产生终端生成的信号,当进程超越了CPU或文件大小的限制时,内核会生成一个信号通知进程。
显式请求:使用kill()函数允许进程发送任何信号给其他进程或进程组。
信号的生成既可以是同步的,也可以是异步的。
同步信号大多数是程序执行过程中出现了某个错误而产生的,由进程显式请求生成的给自己的信号也是同步的。
异步信号是接收过程可控制之外的事件所生成的信号,这类信号一般是进程无法控制的,只能被动接收,因为进程也不知道这个信号会何时发生,只能在发生时去处理它。一般外部事件总是异步地生成信号,异步信号可在进程运行中的任意时刻产生,进程无法预期信号到达的时刻,它所能做的只是告诉Linux内核假如有信号生成时应当采取什么行动(这相当于注册信号对应的处理)
无论是同步还是异步信号,当信号发生时,我们可以告诉linux内核采取以下3钟动作中的任意一种:
忽略信号。大部分信号都可以被忽略,但有两个除外:SIGSTOP和SIGKILL,因为是为了给超级用户提供杀掉或停止任何进程的一种手段。此外,尽管其他信号都可以被忽略,但其中有一些却不宜忽略。例如,若忽略硬件例外(非法指令)信号,则会导致进程的行为不确定。
捕获信号。这种处理是想要告诉Linux内核,当信号出现时调用专门提供的一个函数(信号处理函数,专门对产生信号的事件作出处理)。
让信号默认动作起作用。