信号
1.
概念: 信号是软件中断,提供了一种处理异步事件的方法。
2.
产生信号:
- 终端: 如按Delete键,产生SIGINT信号
- 硬件异常产生信号: 除数为0,无效的内存引用
- 进程调用kill函数
- kill命令
- 软件产生的信号: 如SIGURG, SIGPIPE, SIGALRM(进程设置的定时器超时)
三种处理方式:
- 忽略
- 捕捉
- 执行默认动作
常见信号:
- SIGHUP
- SIGINT: 中断键
- SIGIO: 异步I/O
- SIGSTOP
- SIGTERM: 终止
- SIGCHLD
- SIGPIPE: 管道读进程已终止时写管道,产生此信号
- SIGQUIT: 退出键
3.signal
signal函数:
#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);
成功则返回之前的信号处理配置,失败返回SIG_ERR
func: SIG_IGN, SIG_DFL 或函数地址
进程创建和exec时:
- fork时,子进程继承父进程信号处理方式,因子进程在开始时复制了父进程内存映像。
- exec时,将原先设置为要捕捉的信号更改为默认动作其他信号状态不变(因信号捕捉程序的地址很可能在新程序文件中已无意义)
4.低速系统调用
若进程在执行一个低速系统调用而阻塞,期间捕捉到一个信号时,则该低速系统调用就被中断不再继续执行。该系统调用返回出错,errno设置为EINTR
低速系统调用是可能使进程永远阻塞的一类系统调用,包括:
- 读
- 写
- pause函数和wait函数
- 某些ioctl操作
- 某些进程间通信函数
假定一个读操作被终端,希望重启它,操作如下:
again:
if((n = read(fd, buf, BUFSIZE)) < 0) {
if(errno == EINTR)
goto again;
...
}
- ioctl, read, readv, write, writev只有对低速设备进行操作时才会被信号中断
- wait和waitpid在捕捉到信号时总是被中断
5.可重入函数
如malloc: 进程执行malloc时,若由于捕捉到信号而执行信号处理程序,其后又调用malloc, 可能会对进程造成破坏。因为malloc通常为它所分配的存储区维护一个链表,而插入执行信号处理程序时,进程可能正在更改此链表。
可重入: 异步信号安全的
可重入函数: 略
errno: 信号处理程序可能修改其原先值,可以先在调用前保存errno,调用后恢复errno
6.可靠信号术语
在信号产生(generation)和递送(delivery)之间的时间间隔内,称信号是未决的(pending).
若进程阻塞了信号递送,那么该进程将此信号保持为未决状态,直到该进程对此信号解除了阻塞,或者将对此信号的动作更改为忽略。
大多数UNIX并不对信号排队,而是只传送这种信号一次。
每个进程都有一个信号屏蔽字: 规定了当前要阻塞传送到该进程的信号集.
7.kill和raise
kill: 将信号发送给进程或进程组
raise: 向进程自身发信号
8.alarm和pause
alarm: 设置一个定时器,超时时产生SIGALRM信号
pause: 使调用进程挂起直到捕捉到一个信号
9.信号集
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
10.sigprocmask, sigpending
sigprocmask: 检测或更改信号屏蔽字
sigpending: 返回阻塞信号集
#include <unistd.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
int sigpending(sigset_t *set);
how: ISG_BLOCK, SIG_UNBLOCK, SIG_SETMASK
11.sigaction
sigaction: 检查或修改某信号的处理动作
#include <signal.h>
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
struct sigaction {
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_sigaction)(int, siginfo_t *, void *);
}
更改信号动作时,sa_handler是信号捕捉函数的地址。sa_mask是一个信号集,调用捕捉函数前,它被加到信号屏蔽字中; 信号捕捉函数返回时,再将进程的信号屏蔽字恢复为原先值.
大部分平台用sigaction实现signal函数
12.sigsuspend
sigsuspend: 在一个原子操作中先设置信号屏蔽字,然后使进程休眠。
sigsuspend原子性地设置信号屏蔽字,挂起进程,直到捕捉到一个信号,从信号处理程序返回时,信号屏蔽字恢复为原先的值。它相当于sigprocmask(SIG_SETMASK, sigmask, NULL)和pause()的原子操作,可以防止在sigprocmask和pause之间发生信号,从而信号丢失,这样使得pause永远阻塞。
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
13.函数abort和system
abort: 使程序异常终止,此函数将SIGABRT信号发送给调用进程
system: POSIX.1要求system忽略SIGINT和SIGQUIT,阻塞SIGCHLD
14.函数sleep, nanosleep 和 clock_nanosleep
使进程挂起直到到了定时时间或者进程捕捉到一个信号并从信号处理程序返回.