APUE第10章 信号
信号是异步中的重要概念,进程执行过程中可能会收到各种各样的信号。
信号概念
信号一般SIG作开头,头文件一般在1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16> If sig is 0, then no signal is sent, but error checking is still performed;
this can be used to check for the existence of a process ID or process
group ID.
### 不能捕捉或者忽略的信号
SIGKILL、SIGSTOP这两个信号是不能被忽略或者捕捉的,因为是内核提供的可靠终止的方法。但是SIGTERM可以。SIGTERM可以让程序检测kill命令发出的信号,从而做好清理工作而退出
## signal函数
Unix系统信号机制最简单的接口
```cpp
#include <signal.h>
void (*signal(int signo, v函数(*func)(int)))(int);
//signal函数第一个参数是信号,第二个是回调函数,无返回值,参数为整数
//signal返回值是一个函数指针,就是之前的处理指针,一般会有这样的宏定义
#define SIG_ERR (void (*)())-1
#define SIG_DFL (void (*)())0
#define SIG_IGN (void (*)())1
早期的信号是不可靠的,可能会丢失。
- 早期的信号处理是有时间窗口的,这个窗口内遇到第二个信号就会造成执行默认工作
- 早期如果进程不希望某种信号发生时,不可以关闭该信号
系统中断
当捕捉到某个信号的时候,系统中断中断的是内核中执行的系统调用,而不是函数。
信号处理函数可重入
背景:在信号处理程序中,是无法判断捕捉到信号时进程执行到了哪里,所以要保证在处理程序中只能调用异步信号安全(可重入)的函数。
可重入函数见APUE的P262页
另外由于每个线程只有一个errno,所以就算调用异步信号安全的函数也有可能会改变errno,所以应当在调用前保存errno,调用后恢复errno
可靠信号的一些术语和语义
- 当一个信号产生的时候,内核通常再进程表中以某种形式设置一个标志。
- 信号再产生和递送过程中之间的间隔内,信号是未决的。
- 进程可以阻塞信号传送,如果产生了一个阻塞的信号,且对信号的动作是系统默认动作或捕捉信号,那么该进程讲此信号保持为未决状态,知道该进程解除此信号的阻塞,或设置为忽略。
- 信号在递送信号的时候才决定信号的处理方式,所以再递送前还是可以修改处理动作。
- 进程调用sigpending函数来判定那些信号设置为阻塞切是pending状态(未决状态)
- 如果进程解除阻塞之前,信号发生了多次,POSIX.1允许系统递送信号一次或者多次,但大多数UNIX并不对信号排队,而是只传递一次。
- 如果有多个信号要传递给进程,则传递顺序是不可知的。
- 进程可调用sigprocmas来检测和更改当前信号屏蔽字
kill和raise
- kill将信号发给进程或进程组,而raise允许发给自身
- 发送0信号给其他进程的话,可以用来确定一个特定的进程是否存在,实际不发送信号,如果不存在返回-1,errno为ESRCH。但是由于UNIX会重用进程ID,或当返回时该进程已结束,所以这种测试并没多大意义。
alarm和pause
1 |
|
- 每个进程只能有一个闹钟时间,如果给了新的值,返回值会是上次剩余的时间
- SIGALARM的默认动作是终止进程。如果想捕捉alarm,应该先注册处理函数,再设置alarm,否则窗口时间可能让进程直接终止。
- pause使进程挂起知道捕捉到一个信号,并执行了信号处理程序且返回了,pause才返回。这种情况下pause返回-1,errno为EINTR。
- 使用alarm和pause以及他们的组合,可以实现sleep或其他需要超时的机制,但有很多需要考虑的问题
- alarm的处理与阻塞点的竞争条件,如果系统繁忙,在alarm返回后才执行到阻塞点,那么alarm机制就失效了
- 如果系统调用是自动重启的,如read这种操作并不会被中断
- 如果使用setjmp和longjmp来处理的话虽然可以达到预期效果,但是有可能会跟其他的信号交互出现问题
信号集及其处理函数
1 |
|
使用信号集前要清空或者初始化,否则信号集的状态不可知
1 | //宏实现,若为32位整数表示31个信号的情况下 |
sigprocmask
用于规定阻塞的信号字
1 |
|
如果set是空指针的话,则不改变进程的信号屏蔽字,how的值也就无意义,这时候单纯用来获取当前信号值
如果oset是非空指针的话,当前信号屏蔽字由oset返回
sigpending
用于返回当前pending的信号集
1 |
|
sigaction
取代UNIX的signal函数,用于检查或修改对应信号的处理动作
1 |
|
sigsetjmp和siglongjmp
由于setjmp和longjmp并不保存和恢复信号屏蔽字(FreeBSD8.0和Mac OS X10.6.8中会保存和回复),所以有另外两个函数转门用来做这个事情
1 |
|
当savemask不为0的时候,会保存信号屏蔽字
sigsuspend
原子操作的要求而诞生的,如果想要解除某个信号的阻塞然后pause等待信号发生,由于不是原子操作,有可能会在解除阻塞和pause之间发生,这样的话pause就会永远阻塞了。所以产生了这个sigsuspend的原子操作。
1 |
|
信号应用
一般用于:
- 获取退出信号,进行工作保存,日志记录等
- 父子间的进程间同步
sigqueue
1 |
|
只能发给单个进程,可以使用value参数向信号处理程序传递整数和指针值。
将信号从id转为str
1 | external char *sys_siglist[];//下表就是信号,对应char*的信号名称 |