信号处理
信号作为通知进程发生了某种事件的一种手段,这种时间需要提前请进程注意,并且其发生常常与进程当前的活动无关。信号也称为软中断,它提供了一种处理异步事件的方法,多数应用程序都会用到它。
信号概念
信号是异步传送给进程的一种事件通知,进程无法准确的预测合适会出现信号。产生信号的原因有:
- 用户按下了某个终止键,如ctrl+c|ctrl-等。(中断信号SIGINT)
- 程序异常,如零做除数、非法存储访问等。这种情况一般由硬件检测出,但由内核向发生异常的那个进程发送相应的信号。
- kill函数允许进程发送任何信号给其他进程或进程组。
- 当发生了某种必须让进程知道的事件时也会生成信号。这些事件不是由硬件而是有软条件产生的。
- 企图读写终端的后台进程会得到作业控制信号SIGTTIN或SIGT-TOU
- 当进程超越了CPU或文件大小的限制,内核会生成一个信号。
生成信号归为三大类:程序错误、外部事件和显示请求。
信号的生成既可以是同步的,也可以是异步的。多数程序错误生成的信号是同步的。由进程显示请求而生成的给自己的信号也是同步的。
异步信号是进程之外的事件生成的信号。一般外部事件总是异步的生成信号。异步信号可以在进程运行的任意时刻产生,进程无法预期信号到达的时刻,他所能做的只是告诉内核假如有信号生成
时应当采取什么行动。
当信号发生时,进程可以采取三种动作:
- 忽略信号。
- 捕获信号
- 执行系统默认动作。内核来完成,有五种默认动作:
- 流产:终止进程并且完成内存存贮文件,即写出进程的地址空间内容和寄存器上下文至进程当前目录下名为core的文件中。
- 终止:终止进程但不生成core文件
- 忽略:忽略信号
- 挂起:暂停进程
- 继续:若进程被暂停,回复进程执行,否则忽略此信号
进程可以在任何时候为信号指定一个新动作或回复其默认动作。对于每一种信号,在某个时刻它对应的动作安排称为该信号的布局。
信号的生成和交付
信号处理过程实际上设计两个方面——生成和交付。信号生成发生在出现了一个需要进程注意的事件时,此时内核将检查接受信号进程的有关数据结构,此结构中记录了信号的当前布局、悬挂信号集和处理
动作等内容。如果信号是被忽略的,内核不做任何返回;若不是忽略的,内核则把此信号加到悬挂信号集合中。通常UNIX表示悬挂信号集的数据结构是一个位串,其中每一位对应一个信号,内核无法记录
统一信号的多个实例,因此进程只知道只是少有一个信号的实例正悬挂着。
如果进程正处在可中断的睡眠状态并且这个信号是非阻塞的,内核便唤醒此进程使他能够接受信号。
被唤醒的这个进程一旦运行便返回到它的正常用户态之前先处理所有悬挂信号。当有悬挂信号并且当前没有阻塞时,内核将查看与该信号有关的信息。如果没有指定句柄,它将采取默认动作,通常
是终止进程。如果有句柄,他将把此信号加入到阻塞信号屏蔽中;如果对该信号致命了SA_NODEFER标志,则不把他加到信号屏蔽中。
最后内核安排进程返回到用户态并传递控制给信号句柄,同时保证当句柄完成时,进程将从被信号中断处的代码开始在执行。
由异步事件生成的信号可能在进程代码执行路径的任何一条指令之后发生。当信号句柄完成时,进程从它被信号中断处恢复执行。如果信号在进程正处于系统调用期间到达的,内核通常流产此系统调用
并返回错误EIN-TR。
信号
系统为每一种可能的事件定义了一种信号,每一种信号有一个信号数,每一个信号数对应以字母SIG开头的信号名。
程序错误类信号
程序错误一般指程序作了某种不合法的动作。不过这里所指的程序错误仅仅是那些会导致产生信号的错误,并不是所有错误均生成错误。事实上,大部分错误都不生成错误。如:
打开一个不存在的文件是一个错误,但它不产生信号而是返回-1并指出错误。
当操作系统或计算机硬件本身检测到一个严重的程序错误生成下述信号。一般而言,这些信号表明程序已经以某种方式严重的崩溃,并且无法正常继续运行。
这些信号默认动作使程序流产,即使进程终止同时,写出内存信息转储文件core(记录错误原因)到进程的当前工作目录。
程序中止类信号
用户终止进程的运行。
所有这类信号的默认动作使使进程终止运行。但为了在终止之前能够做一些清场的工作,例如保存状态信息、删除临时文件或恢复终端原来的方式,常常需要处理这些信号。
这类信号的捕获函数应当在处理结束时为这个信号指定默认动作,然后再次生成该信号,从而使得进程以该信号的默认动作而终止,就好像没有经过信号处理函数一样。
- SIGHUP:终端驱动程序检测到连接断开时会生成这个信号给与控制终端相连的控制进程。连接断开可能是由于网络或调至解调器的连线中断而引起。
- CLOCAL标志设置意味着所连终端时本地终端,此时终端驱动程序忽略所有调制解调器状态线。
- SIGINT:当按下中断键(Delete或Ctrl-c)时由终端驱动程序生成的信号。此信号发送给前台进程组的所有进程。
- SIGKILL:无条件立即终止程序的运行。这个信号一般只由显示请求生成,它不能被捕获或忽略,也不能被阻塞,因此它总是致命的。
- SIGQUIT:当在终端按下结束键(Ctrl-)时,终端驱动程序生成此信号。
- SIGTERM:使进程终止运行,此信号由kill(1)命令发出。与SIGKILL不同,该信号可以阻塞、捕获和忽略。这是要求程序终止的常规信号。
闹钟类信号
闹钟类信号用于通告定时器到期。这类信号的默认动作是使程序终止,不过着中默认行为很少用,大多数使用这类信号都设置有一处理信号的句柄函数。
- SIGALRM 通常指出一个度量真实时间或时钟的定时器到期。
- SIGPROF 此信号指出度量由当前进程用去的CPU 时间加上由系统为该进程服务所用去的CPU时间的定时器到期。
- SIGVTALRM 此信号指出度量当前进程用去的CPU时间的定时器到期。
I/O类信号
I/O类信号用于通知进程在描述子上发生了感兴趣的事,用于支持信号驱动的I/O。为生成这类信号。必须调用ioctl()或fcntl()要求特定的文件描述字生成他们。这类信号的默认动作是忽略它们。
- SIGIO:当文件描述字可以执行输入或输出时发送此信号。一般只有终端或网络连接套接字是唯一能够生成此信号的文件。
- SIGPOLL: LINUX中,SIGPOLL等价于SIGIO。
- SIGURG:此信号通知进程出现了紧急事件。当在网络连接上收到带外数据时可以有选择地生成此信号。
作业控制类信号
作业控制类信号用于支持作业控制,仅在支持作业控制的操作系统中起作用。当系统不支持作业控制时,尽管这些信号名有定义,但不会生成信号。
- SIGCHLD:无论何时,只要进程终止或停止就会向其父进程发送SIGCHLD信号。此信号的默认动作是忽略,因此如果父进程想要在子进程状态发生改变时得到通知,她就必须捕获此信号。
- SIGCOMT:使暂停的进程回复运行,它可以发送给任何进程。
- SINSTOP:停止一个进程,它不能捕获、阻塞或忽略。
- SIGTSTP:当用户在终端按下暂停键(ctrl-z)时,由终端驱动程序生成的交互式停止信号。
- SIGTTIN:后台进程不能读他的控制终端,若企图读他的控制终端,终端驱动程序会生成此信号。
- SIGTTOU:后台进程企图写他的控制终端,终端时由终端驱动程序生成的信号。
多数应用程序不需要处理这些信号,由交互shell完成处理这些信号需要的所有工作。
操作错误类信号
操作错误类信号报告程序进行的某种操作所产生的错误,这些错误不一定是编程错误,而是妨碍操作系统完成系统调用的错误。默认行为是使程序终止。
- SIGPIPE:管道破裂。当使用管道或FIFO时,读管道的进程必须先于另一进程写管道之前打开管道。如果读管道的进程从未开始或不曾预料的终止,则写管道或FIFO的进程将导致高信号。如果写管道的进程阻塞或忽略了SIGPIPE信号,则写操作不会产生该信号,而是返回EPIPE于errno而失败。
- SIGXCPU:进程超过了它的软CPU时间限制时生成此信号。
- SIGXFSZ:进程企图扩大文件以至于超过了软文件大小限制时生成此信号。