信号可以理解成一种软件中断。他提供了一种异步处理事件的方式。每个信号都有一个与之对应的信号名,这些信号名都带有SIG前缀,如:SIGABRT,SIGALARM。头文件signal.h 中定义了所有的信号名,他们值为正整数常量。事实上,实现将个别信号定义在不同的头文件中,只不过这些头文件又被包含在了signal.h中;这是因为内核不可能去包含应用于用户级别程序的头文件!因此,当用户程序与内核同时需要某信息的定义时,通常的做法是把这个定义放到内核头文件中,然后在用户头文件中包含这个内核头文件。
unix系统信号列表:
当信号列表中的默认action为“terminal+core”时,他意味着进程的内存映像会留在进程目录下的core文件中。core文件可以帮助大多数UNIX系统调试者来检查进程终止时的状态。但是,如果 a)进程设置了set-user-ID而且当前用户不是进程文件的拥有者;或者 b)进程设置了 set-group-ID 而且当前用户不是进程文件的用户组拥有者;或者 c) 当前用户下该用户没有写入权;或者d) 此文件已存在而且当前用户没有写入权;或者e)文件太大时core文件不会被创建。
程序启动
当一个程序被执行的时候,所有的信号的装填要么是默认方式处理要么是忽略信号。通常,信号被设置为它们的默认处理方法,除非调用exec的进程忽略了这个信号。详细来说exec函数会将调用exec进程捕获的信号的状态更改为信号的默认处理方式而保留其他信号的处理方式,因为exec执行的新程序中不包含捕获信号的函数地址,所以这些处理方式在新程序中是无意义的。
进程创建
当一个进程调用fork时,子进程继承父进程的信号处理方式。在这里,子进程是由父进程的内存镜像的副本开始的,因此信号捕获函数的地址是有意义的。
可重入函数
当一个信号一个进程的信号处理函数捕获,此进程的正常指令执行顺序会被此信号处理短暂的中断,处理完信号后进程从之前被中断的地方继续执行。但是在信号处理函数中,我们是无法识别出当信号被捕获是进程执行到了哪一步。如果当收到信号时进程正在通过malloc从堆上分配一块额外的内存时,我们应该怎么办呢,在信号处理函数中调用malloc吗?亦或是当收到某个信号时我们正在调用某个函数,比如getpwnam,这个函数将他的返回值存储在一个静态区域,这种情况下我们应该在信号处理函数中调用同样的函数吗?在上面malloc的情景中那样做会对进程产生灾难性的后果,因为malloc通常包含一个它所有分配过的区域的链接表,也许当时它正处于更新这个链接表的状态中。在getpwnam的情境下,存储在静态区的getpwnam的返回值会被信号处理函数中的调用结果重写!
因此, The Single UNIX Specification 要求信号处理函数中的函数调用必须是安全的,即可重入的(Reentrant functions)。这些函数被 The Single Unix Specification称为异步信号安全函数(async-signal safe)。除了可重入,他们会在函数运行期间阻塞任何会破坏连续性的信号的下达。异步信号安全函数列表如下:
一些函数不被称为异步信号安全的原因大致如下:
- 使用了静态数据结构
- 调用了malloc或free
- 属于标准I/O库的一部分
大多数标准I/O库的实现使用了静态数据结构,他们都不是可重入函数。需要特别注意的一点是:即使我们在信号处理函数中使用异步信号安全的函数,每个线程下也仅仅只有一个errno变量(在多线程环境下,多个线程共享进程地址空间。每个线程需要它自己的errno副本以阻止线程间的相互干扰),而我们可能会潜在的修改掉errno的值。因此,有个通则:在信号处理函数中调用异步信号安全函数前,应当保存errno。