信号
常见信号
- SIGABRT:异常终止,默认动作:终止+core.
- SIGALRM:定时器超时,终止
- SIGCHLD:子进程状态改变,忽略
- SIGINT:终端中断符,终止
- SIGKILL:终止,终止
- SIGPIPE:写至无读进程的管道,终止
- SIGQUIT:中断推出符,终止+core
- SIGSTOP:停止,停止进程
- SIGTERM:终止,终止
值得注意的是,我们可以为很多信号编写自己的信号处理函数,但是对于SIGSTOP和SIGKILL,它们既不能被捕捉也不能被忽略。
几种终止信号的比较
SIGINT:当用户按中断键,终端驱动程序产生此信号并发送到前台进程组中每一个进程。
SIGABRT:调用abort产生此信号,进程异常终止。
SIGKILL:不能被忽略或捕捉的信号,杀死一个进程可靠的方法。
SIGQUIT:相比于SIGINT,同时产生一个core文件。
SIGTERM:kill命令的默认终止信号。SIGTERM让程序有机会在推出前做好清理工作,从而优雅的终止。
SIGSTOP:作业控制信号,停止一个进程,类似于交互停止信号。不能被忽略或捕捉。
常用Api
1 |
|
1 |
|
raise(signo)等价于调用kill(getpid(),signo)
进程将信号发送给其他进程需要权限,超级用户可以将信号发送给任一进程。对于非超级用户,基本规则是发送者的实际用户ID或有效用户ID必须等于接收者的实际用户ID或有效用户ID。(谁登录,执行进程的实际ID就是谁)
1 |
|
每个进程只能有一个闹钟时间,以前注册的时间将作为新闹钟的返回值返回。如果seconds为0,相当于取消以前的闹钟。alarm的默认动作是终止进程,我们必须在alarm之前安装处理程序,不然可能出现意外情况。
1 |
|
只有执行了信号处理程序并从其返回时,pause才返回。
1 |
|
1 |
|
how是可选的选项,包括SIG_BLOCK,SIG_UNBLOCK,SIG_SETMASK。注意:不能阻塞SIGKILL和SIGSTOP信号。
1 |
|
sigpending返回当前未决的信号。
1 |
|
在信号处理期间将sa_mask加入阻塞集,在调用完后恢复。若同一个信号发生多次,通常不会将它们加入队列,被阻塞的信号发生多次,在恢复后通常只会被调用一次。sa_flags能决定对于信号中断的系统调用是否重启,通过设置SA_INTERRUPT或SA_RESTART.
1 |
|
之所以增加对于信号的跳转函数的特殊处理,是因为在信号处理函数里的屏蔽信号跟进程环境是有区别的。当savemask非0,则调用sigsetjmp时在env中保存当前信号屏蔽字。在siglongjmp后恢复。
1 |
|
这个函数是为了表达进入休眠,期待被期望的信号唤醒的语义。假想在没有该函数时,我们该如何实现。为期待的信号设置阻塞,然后解除阻塞后紧接着调用pause()?那要是确实有期待的信号被阻塞了,但是在接触阻塞后,调用pause前解除了,可能就存在永远唤不醒的情况。
进程的信号屏蔽字设置为由sigmask指向的值。如果捕捉到一个信号而且从信号处理程序返回,则sigsuspend返回,并恢复原来的屏蔽字。
1 |
|
调用此函数将给进程发送一个SIGABRT信号。让进程捕捉SIGABRT意图是:在进程终止之前由其执行所需的清理操作。如果进程不在信号处理程序中终止自己,POSIX对此的要求是当信号处理程序返回时,abort终止该进程。POSIX对终止进程提出的要求是,所有打开的标准流应当与对每个流调用fclose相同。
1 |
|
函数使进程被挂起直到满足下列两个条件之一:
- 过了seconds指定的时间。
- 调用进程捕捉到了一个信号并从信号处理程序返回。
有关信号的一些小内容
可重入函数
我对信号处理函数的理解是像线程一样,它和主线程是共享全局环境的。因此在信号处理函数中调用可重入函数就显得很重要。函数的不可重入性往往是因为如下原因a)使用静态数据结构;b)调用malloc或free;c)它们是标准I/O函数,标准I/O库很多都使用全局数据结构。
信号处理函数虽然像线程,但是线程保存着errno的副本,互不干扰。但信号处理函数不一样,因此,我们在调用一些系统函数时,需要先保存errno值,在调用完后恢复。
SIGCLD语义
SIGCLD和SIGCHLD不一样,前者时System V的信号名,与POSIX采用的SIGCHLD不同。System V的该信号有如下特点。
- 如果进程明确将信号配置为SIG_IGN,则调用进程的子进程不产生将死进程。
- 如果SIGCLD被设置为捕捉,则内核会立即检查是否有子进程准备好。如果是,调用SIGCLD。
可靠信号语义
当一个信号产生时,内核通常在进程表中以某种形式设置一个标志。在信号产生和递送之间的时间间隔里,信号是未决的。内核在将信号递给进程时,才决定处理方式,在此之前可以任意修改对此信号的处理动作。在进程解除对某个信号的阻塞前,这种信号发生多次,Posix允许递送信号一次或多次。除非支持Posix实时扩展,否则大多数UNIX不对信号排队,只递送信号一次。