信号是软件终端,提供了一种处理异步事件的方法。
一、信号概念
很多条件可产生信号:
- 用户终端按键。如Ctrl+C产生中断信号SIGINT
- 硬件异常产生信号。除数为0或引用无效内存等,通常由硬件检测到并通知内核,然后内核向进程产生信号。如对执行无效内存引用的进程发送SIGSEGV
- 调用kill(2)函数将任意信号发送给另一个进程或进程组
- 检测到某种软件条件已经发生,并应将其通知有关进程时,产生信号。例如在网络连接上传来的带外数据(SIGURG)、在管道的读进程终止后,一个进程写此管道(SIGPIPE)以及进程所设定的定时器已经超时(SIGALARM)
信号的处理有三种方式:
(1)忽略此信号。但有两种信号不能忽略:SIGKILL和SIGSTOP。原因是:它们向内核提供了使进程终止或停止的可靠方法。
(2)捕捉信号。自定义回调函数处理
(3)执行系统默认动作。大多数信号的默认动作是终止进程
部分信号默认动作中,会在进程当前目录的core文件中复制该进程的内存映像,可用于调试程序。
常见信号:
- SIGABRT:调用abort函数时产生此信号。进程异常终止
- SIGALRM:当用alarm函数设置的定时器超时,产生此信号。setitimer(2)函数设置的间隔时间超时,也产生此信号
- SIGCHLD:在一个进程终止或停止时,SIGCHLD信号被发送给父进程,系统默认忽略此信号。若父进程需要知道子进程的状态改变,则应捕捉此信号
- SIGHUP:若终端接口检测到一个连接断开,则此信号发送给与终端相关的控制进程
- SIGPIPE:在管道的读进程已终止时写管道,则产生此信号。当SOCK_STREAM套接字不再连接时,进程写该套接字也产生此信号
- SIGTERM:这是由kill(1)命令发送的系统默认终止信号。由于该信号是由应用程序捕获的,使用SIGTERM也让程序有机会在退出前做好清理工作
二、函数signal
signal函数由ISO C定义。因为ISO C不涉及多进程、进程组以及终端I/O等,所以它对信号的定义非常含糊。很多不同的UNIX版本的实现不同。所以最好使用sigaction代替
三、中断的系统调用
早期UNIX系统的一个特性是:如果进程在执行一个低速系统调用而阻塞期间捕捉到一个信号,则该系统调用就被中断不再继续执行。该系统调用返回出错,errno置为EINTR。
为了支持这种特性,将系统调用分为两类:低速系统调用和其他系统调用。
低速系统调用是可能会使进程永远阻塞的一类系统调用,包括对文件(广义,指的是fd)进行读写、pause和wait函数、进程间通信函数等。
读写操作中的一个例外是与磁盘I/O相关的系统调用。虽然读写一个磁盘文件可能暂时阻塞调用者,但是除非发生硬件错误,否则I/O操作总会很快返回,并使调用者不再处于阻塞状态。
若调用出错,我们仍希望继续执行该调用,则必须人为处理出错,重新启动。
为了帮助应用程序不必处理被中断的系统调用,4.2BSD引进了某些被中断系统调用的自动重启动。自动重启动的系统调用包括:ioctl、read、readv、write、writev、wait和waitpid。
四、函数kill和raise
kill函数将信号发送给进程或进程组,raise函数则允许进程向自身发送信号。
#include <signal.h>
int kill