APUE 第10章 信号

来源:互联网 发布:人工智能在制造业应用 编辑:程序博客网 时间:2024/05/22 13:26

第10章 信号

信号是软件中断,定义于在头文件”signal.h”中。

不存在编号为0的信号,kill对0有特殊应用,此种信号称为空信号。

产生信号的条件:
- 某些终端键
- 硬件异常
- kill函数可发送任意信号
- kill命令
- 软件信号

注册信号处理函数

void (*signal(int signo, void (*func)(int))) (int);//等价于typedef void Sigfunc(int);Sigfunc *signal(int signo, Sigfunc *func);

signo的值必须是有效的信号名,func的值是常量SIG_IGN,SIG_DFL或者处理函数的地址,SIG_IGN表示忽略,SIG_DFL表示系统默认动作。函数的返回值为之前使用的func。

应当注意,kill函数和kill命令的术语是不准确的,它只是将一个信号发送给进程或进程组,是否终止则由信号类型决定。

当执行一个程序(exec),所有信号状态将被设为系统默认或忽略,即原先设为捕捉的将都变成默认,因为新程序一般无法使用原有信号捕捉函数的地址了。
而fork一个进程,则子进程继承父进程的信号处理方式。

signal函数的缺陷是不改变信号的处理方式就不能确定信号的当前处理方式。

如果进程在执行一个低速系统调用而阻塞期间捕捉到一个信号,则该系统调用就被中断不再继续执行,例如pause和wait,某些ioctl等。该系统调用返回出错,其errno设为EINTR。有的系统调用被设置为自动重启动的,可以关闭。

进程捕捉到信号并对其进行处理时,进程正在执行的正常指令序列就被信号处理程序临时中断,首先执行该信号处理程序中的指令。在信号处理函数中应该调用安全的函数,这些函数是可重入的并且异步信号安全的。不可重入的函数因为(a)使用静态数据结构(b)调用malloc或free(c)标准I/O.

可重入函数主要用于多任务环境中,可重入函数也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有多个该函数的副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。如果确实需要访问全局变量(包括static),一定要注意实施互斥手段。可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。

在其他信号之前递送与进程当前状态有关的信号。每个进程都有一个信号屏蔽字,它规定了当前要阻塞递送到该进程的信号集,可调用sigprocmask来检测和更改这个屏蔽字。定义的sigset_t类型的二进制位数可以容纳一个信号集。

sigpromask()的how参数可以选择SIG_BLOCK,是或操作,SIG_UNBLOCK是先取反set再与操作,SIG_SETMASK是赋值操作。

kill发送信号给指定进程(组),raise则向自己发送信号。进程发送信号需要权限,pid的取值可以决定要发送的对象。

5个用于处理信号集(sigset_t)的函数:
- sigemptyset():初始化,清除所有
- sigfillset():初始化,包含所有
- sigaddset():增
- sigdelset():删
- sigismember():查

sigaction()提供了更为高级的检查和修改信号处理函数的方法。实现了可以指定系统调用被中断后是否重启动,可以为每个处理函数指定屏蔽字等功能。很多实现使用sigaction实现signal函数。本信号也将被临时加到进程的屏蔽字中,这样就能在调用信号处理函数时阻塞该信号,直到退出才改回来,也可以选择不阻塞,此时就退化成了不可靠信号了。

非局部转移setjmp和longjmp经常用于信号处理函数,但是正如上面所说,直接跳出但不改回信号屏蔽字,则将再也无法收到该信号。所以应该使用sigsetjmp和siglongjmp,它们可以控制保存和恢复信号屏蔽字。

如果希望对一个信号解除阻塞,sigsuspend是一个原子操作,将信号屏蔽字设置为一个指定值,在捕捉到一个信号或发生一个会终止进程的信号之前,阻塞,如果捕捉到且从该信号的处理函数返回,则sugsuspend返回,并将该进程的信号屏蔽字设置为调用sigsuspend之前的值。它的一种应用是等待一个信号处理函数设置一个全局变量。

如果调用kill使其为调用者产生信号,并且该信号不被阻塞,则在kill返回前该信号就被传送给该线程,且从该信号处理程序返回了。

不同的shell会定义自己的返回值,在编写使用system函数的程序时,一定要正确地解释返回值。

sleep在已过时间后返回,或者捕捉到一个信号并从信号处理函数返回后返回。不应使用alarm函数实现,以免互相影响。

nanosleep()

clock_nanosleep()除了相对时间还可以设置绝对时间,这就保证因分时系统导致的相对时间精度问题被解决了。

sigqueue可以对信号进行排队。

作业控制信号:
- SIGCHLD:子进程停止或终止
- SIGCONT:如果进程已停止,则继续其
- SIGSTOP:停止(不能被捕捉或忽略)
- SGITSTP:交互式停止
- SIGTTIN:后台进程组读终端
- SIGTTOU:后天进程组写终端

大多数应用程序只处理SIGCHLD,交互式shell则处理全部。例外:需要管理终端的进程,如vi,当用户挂起它,它需要了解这一个信号并将终端恢复成启动它之前的情况,恢复它也需要将终端恢复成它希望的状态。

sys_siglist[]将信号编号和信号名进行映射,可以使用一些函数打印与编号对应的信号信息。信号编号就是信号的值。

原创粉丝点击