signal

信号处理

UNIX操作系统将信号作为通知进程发生了某种事件的一种手段,这种事件需要提请进程注意,并且其发生常常与进程当前的活动无关。信号也称为软中断,它提供了一种处理异步事件的方法,多数应用程序都需要用到它。例如,用户在终端按下Ctrl-c键会产生一个中断信号(SIGINT)并终止当前运行的程序。信号也可用于进程之间的通信和实现同步原语。本章我们先介绍信号有关的基本概念,然后着重讲述各种信号的用法和编程方法。

信号概念

信号是异步传送给进程的一种事件通知,进程无法准确地预测何时会出现信号。产生信号的原因有多种,例如:
1)用户按下了某个终止键,如Ctrl-\或Ctrl-c等。在这种情况下,终端驱动程序会向当前进程发送一个中断信号(SIGINT)来停止正在运行的程序。
2)程序异常,如零作除数、非法存储访问等。这种情况一般由硬件检测出,但由内核向发生异常的那个进程发送相应的信号。例如,SIGSEGV是对执行了非法存储访问的进程生成的信号。
3)kill函数允许进程发送任何信号给其他进程或进程组。
4)当发生了某种必须让进程知道的事件时也会生成信号。这些事件不是由硬件而是由软条件产生的。例如,当网络连接上到达了带外数据(12.8节)时生成的SIGURG,当进程设置的定时器到期时生成的SIGALRM,以及当进程往一个管道(11.1节)写数据,而此管道已不存在读数据方时生成的SIGPIPE等。
5)企图读写终端的后台进程会得到作业控制信号SIGTTIN或SIGTTOU。
6)当进程超越了CPU或文件大小的限制时,内核会生成一个信号。

生成信号的事件可以归为三大类:程序错误、外部事件和显式请求。例如,前面列出的第2)种和第6)种情况为程序错误,第3)种情况为显式请求,其他的均为外部事件。

信号的生成既可以是同步的,也可以是异步的。同步信号与程序中的某个具体操作相关并且在那个操作进行的同时产生。多数程序错误生成的信号是同步的。由进程显式请求而生成的给自己的信号也是同步的。

异步信号进程之外的事件生成的信号。一般外部事件总是异步地生成信号。异步信号可在进程运行中的任意时刻产生,进程无法预期信号到达的时刻,它所能做的只是告诉内核假如有信号生成时应当采取什么行动。

无论是同步还是异步信号,当信号发生时,进程可以采取如下三种动作中的任意一种:
1)忽略信号。大部分信号都可以忽略,但SIGSTOPSIGKILL除外,这两个信号决不会被忽略。不能忽略这两个信号的原因是为了给超级用户提供杀掉或停止任何进程的一种手段。
2)捕获信号。捕获信号即对信号进行专门的处理。此时要告诉UNIX内核,当信号出现时调用进行专门处理的函数。这个函数称为信号捕获函数,也称为信号句柄或简称句柄。例如,假如我们编写的是一个命令解释程序,就会需要处理用户键盘生成的中断信号,并在接收到这种信号时中断正在为用户执行的命令并返回到程序的主循环准备接收下一命令。
3)执行系统默认动作。系统为每种信号规定了一个默认动作,这个动作由UNIX内核来完成。有五种可能的默认动作:
流产:终止进程并且生成内存转储文件,即写出进程的地址空间内容和寄存器上下文至进程当前目录下名为core的文件中。
终止:终止进程但不生成core文件。
忽略:忽略信号。
挂起:暂停进程。
继续:若进程被暂停,恢复进程执行,否则忽略此信号。

进程可以在任何时候为信号指定一个新动作或恢复其默认动作。对于每一种信号,在某个时刻它对应的动作安排称为该信号的布局。

UNIX信号处理过程实际上涉及两个方面—生成和交付。信号生成发生在出现了一个需要进程注意的事件时,此时内核将检查接收信号进程的有关数据结构,此结构中记录了信号的当前布局、悬挂信号集和处理动作等内容。如果信号是要被忽略的,内核不做任何动作便返回;若不是忽略的,内核则把此信号加到悬挂集合中。通常UNIX表示悬挂信号集的数据结构是一个位串,其中每一位对应一个信号,内核无法记录同一信号的多个实例,因此进程只知道至少有一个该信号的实例正悬挂着。

如果进程正处在可中断的睡眠状态并且这个信号是非阻塞的,内核便唤醒此进程使它能够接收信号。此外,作业控制信号,如SIGSTOP或SIGCONT将直接挂起或恢复进程而不是发送出去。

被唤醒的这个进程一旦运行便在返回到它的正常用户态之前先处理所有悬挂信号。它让内核代表它查看是否有悬挂信号,当有悬挂信号并且当前没有被阻塞时,内核将查看与该信号有关的信息。如果没有指定句柄,它将采取默认动作,通常是终止进程。如果有句柄,它将把此信号加入到阻塞信号屏蔽中;如果对该信号指明了SA_NODEFER标志,则不把它加到信号屏蔽中。类似地,如果指明了SA_RESETHAND标志,则恢复该信号的动作为SIG_DFL(关于SA_NODEFER和SA_RESETHAND标志,参见7.4.4节)。

最后,内核安排进程返回到用户态并传递控制给信号句柄,同时保证当句柄完成时,进程将从被信号中断处的代码开始执行。

由异步事件生成的信号可能在进程代码执行路径的任何一条指令之后发生。当信号句柄完成时,进程从它被信号中断之处恢复执行。如果信号是在进程正处在系统调用期间到达的,内核通常流产此系统调用并返回错误EINTR(7.9.3节)。

本章中,我们经常会涉及一些与信号有关的术语。当发生了一个需要引起进程注意的事件而导致信号出现时,我们称对此进程生成信号或发送信号。这些事件可以是硬件异常,软条件、终端产生的信号或调用kill()函数产生的信号。当信号生成时,内核通常在进程表中设置某种形式的标志。

当被发送信号的那个进程识别到了信号并采取了适当动作时,我们称信号被交付给了进程,或称进程接收了信号。若信号交付时进程执行信号句柄,我们称进程捕获了信号。当一个信号已经生成,但还未交付时,称该信号是悬挂的。

进程接收一个信号时的动作取决于该信号的当前布局和进程的信号屏蔽字。每个进程有一个信号屏蔽字标识进程当前被阻塞交付的信号集合,它是一个位串,其中每一位对应一个信号。如果某个信号对应的位被设置,则该信号是当前被阻塞的。

进程可以有选择地阻塞信号的交付。阻塞信号与忽略信号完全不同,忽略信号是不理睬发生的信号,而阻塞信号只是暂时地推迟信号的交付,直到我们准备好了时再对它进行处理。当一个被阻塞的信号生成时,如果进程为该信号指定的动作是默认动作或捕获它,则此信号将一直悬挂直到对此信号的阻塞被放开,或者改变该信号的动作为忽略。系统对怎样处理阻塞信号的判定是在信号被交付时而不是信号生成时,这样便可以允许进程在信号被交付之前改变信号的动作。

UNIX 信号

UNIX系统为每一种可能的事件定义了一种信号,每种信号有一个信号数,每一个信号数对应一个以字母SIG开头的信号名。例如,SIGABRT是进程生成的流产信号,SIGALRM是由定时器到期所生成的闹钟信号。

函数psignal()可以打印出信号数对应的描述信息至标准错误输出流。

#include <signal>
void psignal (int signo, const char *msg);

若msg是空指针,psignal()只打印signo对应的描述并后随一换行符。

若msg是非空指针,psignal()用这个字符串(通常为程序名)作为其输出消息的前缀,并且在此前缀和signo对应的消息之间放置一个冒号和空格将它们分开。

程序错误类信号

程序错误一般指程序执行了某种不适合的动作。并不是所有程序错误都会生成信号,事实上大部分错误都不会生成信号。例如,打开一个不存在的文件是一个错误,但它不产生信号,而是返回-1指出错误。这里的程序错误仅仅是指那些会导致产生信号的错误。

当操作系统或计算机硬件检测到一个严重的程序错误时会生成下述信号。一般而言,这些信号表明程序已经以某种方式严重地崩溃而无法正常继续运行。

所有这些信号的默认动作都是使进程流产,即在终止进程的同时写出内存信息转储文件core。这样用户便可通过调试器来查出引起错误的原因。

SIGABRT:调用abort()函数生成的流产信号。

SIGFPE:致命算术异常。此信号名源于浮点异常(floating point exception),实际上它涵盖了所有算术异常,包括零作除数和浮点溢出。

SIGILL:进程执行了一条非法指令或企图执行特权指令。此信号名由非法指令(illegal instruction)而来。经编译器生成的执行文件只含合法指令,一般出现SIGILL信号的原因是可执行文件被破坏或者程序企图执行数据。

SIGBUS:实现定义的硬件错误,其名字来源于总线错误(bus error),它一般指硬件地址错。SIGBUS信号与SIGSEGV信号的区别在于SIGSEGV指出的是对合法存储地址的非法访问(如访问不属于自己的存储空间或只读存储空间),而SIGBUS指的是硬件地址非法。

SIGIOT:实现定义的硬件错误,其名字来源于PDP-11输入输出自陷指令iot。

SIGSEGV:非法存储访问,其名字源于段访问错误(segmentation violation)。当进程企图读写分配给它的存储区之外的单元或写只读存储区时产生这个信号(实际上,仅当所访问的单元偏离程序的存储区相当远而使得系统的存储保护机制能检测出时才发生此信号)。导致SIGSEGV信号的原因常常是由于引用了一个空的或未赋初值的指针,或由于数组越界而引起。

SIGSYS:非法系统调用。由于某种未弄清的原因,进程执行了一条被内核认为是系统调用的机器指令,但随该指令一起指明系统

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值