信号
信号(signal)机制是UNIX系统中最为古老的进程之间的通信机制。它用于在一个或多个进程之间传递异步信号。信号可以由各种异步事件产生,例如键盘中断等。Shell也可以使用信号将作业控制命令传递给它的子进程。
Linux系统中定义了一系列的信号,这些信号可以由内核产生,也可以由系统中的其他进程产生,只要这些进程有足够的权限。可以使用kill命令(kill -l)在机器上列出所有的信号,如下所示:
进程可以屏蔽掉大多数的信号,除了 SIGSTOP和SIGKILL。SIGSTOP信号使一个正在运行的进程暂停,而信号SIGKILL则使正在运行的进程退出。进程可以选择系统的默认方式处理信号,也可以选择自己的方式处理产生的信号。信号之间不存在相对的优先权,系统也无法处理同时产生的多个同种的信号,也就是说,进程不能分辨它收到的是1个或者是42个SIGCONT信号。
□ SIGABRT:调用abort()函数时产生此信号,进程异常终止。
□ SIGALRM:超过alarm()函数设置的时间时产生此信号, 定时器终止时发送给进程的信号。
□ SIGBUS:指示一个实现定义的硬件故障。当出现某种类型的内存故障时,实现常常产生此种信号。就实际编程中,遇到这个信号往往是如下的情况:
1. 未对齐的地址访问
2. mmap时,访问映射区已经不存在的部分(如用文件长度映射了一个文件,但在引用该映射区之前,另一个进程已将该文件截断)
□ SIGCHLD:在一个进程终止或停止时,SIGCHLD信号被送给其父进程。如果希望从父进程中了解其子进程的状态改变,则应捕捉此信号。信号捕捉函数中通常要调用wait()函数以取得子进程ID和其终止状态。
□ SIGCONT:此作业控制信号送给需要继续运行的处于停止状态的进程。如果接收到此信号的进程处于停止状态,则操作系统的默认动作是使该停止的进程继续运行,否则默认动作是忽略此信号。
□ SIGEMT:指示一个实现定义的硬件故障。
□ SIGFPE:此信号表示一个算术运算异常,例如除以0,浮点溢出等。
□ SIGHUP:如果终端界面检测到一个连接断开,则将此信号送给与该终端相关的进程。
□ SIGILL:此信号指示进程已执行一条非法硬件指令。
□ SIGINT:当用户按中断键(一般采用Delete或Ctrl+C)时,终端驱动程序产生这个信号并将信号送给前台进程组中的每一个进程。当一个进程在运行时失控,特别是它正在屏幕上产生大量不需要的输出时,常用此信号终止它。
□ SIGIO:此信号指示一个异步IO事件。
□ SIGIOT:这指示一个实现定义的硬件故障。
□ SIGPIPE:如果在读进程时已终止写管道,则产生此信号。
□ SIGQUIT:当用户在终端上按退出键(一般采用Ctrl+C)时,产生此信号,并送至前台进程组中的所有进程。
□ SIGSEGV:指示进程进行了一次无效的存储访问。
□ SIGSTOP:这是一个作业控制信号,它停止一个进程。
□ SIGSYS:指示一个无效的系统调用。由于某种未知原因,某个进程执行了一条系统调用命令,但是调用命令所用的参数无效。
□ SIGTERM:这是由kill命令发送的系统默认终止信号。
□ SIGTRAP:指示一个实现定义的硬件故障。
□ SIGTSTP:交互停止信号,当用户在终端上按挂起键(一般采用Ctri+Z)时,终端驱动程序产生此信号。
□ SIGTTIN:当一个后台进程组进程试图读其控制终端时,终端驱动程序产生此
信号。
□ SIGTTOU:当一个后台进程组进程试图写其控制终端时产生此信号。
□ SIGURG:此信号通知进程己经发生一个紧急情况。在网络连接上,接到非规定波特率的数据时,此信号可选择地产生。
□ SIGUSR1:这是一个用户定义的信号,可用于应用程序。
□ SIGUSR2:这是一个用户定义的信号,可用于应用程序。
1. 信号截取函数signal()
signal()函数用于截取系统的信号,对此信号挂接用户自己的处理函数。其原型如下:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signal()函数的原型说明此函数要求两个参数,返回一个函数指针,而该指针所指向的函数无返回值(void)。
第1个参数signo是一个整型数,第2个参数是函数指针,它所指向的函数需要一个整型参数,无返回值。用一般语言来描述就是要向信号处理程序传送一个整型参数,而它却无返回值。当调用signal设置信号处理程序时,第2个参数是指向该函数(也就是信号处理程序)的指针。signal的返回值指向以前信号处理程序的指针。
如下代码截取了系统的信号SIGSTOP和SIGKILL,用命令kill杀死其是不可能的。
2. 向进程发送信号函数kill()和raise()
在挂接信号处理函数后,可以等待系统信号的到来。同时,用户可以自己构建信号发送到目标进程中。此类函数有kill()和raise()函数,函数的原型如下:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
int raise(int sig);
kill()函数向进程号为pid的进程发送信号,信号值为sig。当pid为0时,向当前系统的所有进程发送信号sig,即“群发”的意思。raise()函数在当前进程中自举一个信号sig,即向当前进程发送信号。
注意:kill()函数的名称虽然是“杀死”的意思,但是它并不是杀死某个进程,而是向某个进程发送信号,这个信号除了 SIGSTOP和SIGKILL,一般不会使进程显式地退出。