第10章 信号
信号是软件中断,提供处理异步事件的方法。
UNIX早期版本就已经提供了信号机制,但是不可靠。(信号可能丢失,执行临界区代码时,进程很难关闭选择的信号)4.3BSD和SVR3增加了可靠信号机制。POSIX.1对可靠信号例程进行了标准化。
- 信号的概念
信号都有一个名字,并且都已SIG开头。
每个不同的Linux版本拥有不同的信号类型个数。
在头文件<signal.h>中,信号被定义为正整数。(信号编号)
产生信号的条件
- 当用户按下某个终端按键,引发终端信号。可以用来停止已经失控的程序。
- 硬件异常产生中断:除以0,或者无效的内存。这些条件通过硬件检测到,并将其通知内核,然后内核为该条件发生时正在运行的进程产生适当的信号。
- 进程调用kill(2)函数可将信号发送给另一个进程或者进程组。有限制,接受者和发送者的所有者必须相同,或者发送者的的所有者必须是超级用户。
- 用户可用kill(1)命令讲信号发送到指定的进程。常用来终止后台运行的进程。
- 当检测到某种软件条件已经发生,并应将其通知有关进程时也产生信号。如网络连接上数据的到达SIGUR
进程不能简单的测试变量来辨别是否出现信号,而是告诉内核“在此信号出现时,应该怎么做”
进程要求信号的处理
- 忽略此信号,两种信号不能忽略,SIGKILL和SIGSTOP,原因:他们向超级用户提供使进程停止的方法。
- 捕捉信号。要通知内核在某种信号发生时调用一个用户函数。用户函数中执行这种事件的处理程序。不能捕捉上边两种信号。
- 执行系统默认动作。默认动作,“终止+core”表示在进程当前工作目录的core文件中复制该进程的存储镜像。
下列条件不产生core文件
- 进程是设置用户ID的,而且当前用户并非程序文件的所有者
- 进程是设置组ID的,而且当前用户并非该程序文件的组所有者
- 用户没有写当前目录的权限
- 该文件已经存在
- signal函数
#include<signal.h>
void (*signal (int signo,void ( *func )( int ) ) )( int );
signo参数是信号名,func的值是常亮SIG_IGN,常量SIG_DFL或者接到此信号后要调用的函数的地址。
- SIG_IGN忽略此信号
- SIG_DFL系统默认动作
- 当为函数地址时,这种处理成为“捕捉”,此函数成为信号处理程序或信号捕捉函数
返回一个函数指针,指向的函数无返回值。指向之前信号处理程序的指针。
- 程序启动
当执行一个程序时,所有信号的状态都是系统默认或忽略。所有信号被设置为他的默认动作,除非调用exec的进程忽略此信号。exec将原来设置为捕捉的信号改为默认动作,是因为信号捕捉函数的地址很可能在所执行的新程序文件中已经没有了意义。
比如shell后台启动一个程序,自动将中断和退出信号的处理方式设置为忽略。于是,按中断键就不会影响到后台进程。
if(signal (SIGINT,SIG_IGN) != SIG_IGN )
signal(SIGINT, sig_int);
这样处理后当信号当前未被忽略时,进程才会捕捉他们。
- 进程创建
当进程调用fork时,其子进程进程父进程的信号处理方式。因为子进程在开始时复制了父进程的存储映像,所以信号捕捉函数的地址在子进程中是有意义的。
- 不可靠的信号
信号可能会丢失:一个信号发生了,但进程却可能一直不知道这一点。有时候希望通知内核阻塞一个信号,不要忽略他,在发生是记住它,然后等进程做好准备再通知它。这种阻塞信号的能力当时并不具备。
早期的问题是,进程每次接收到信号对其进行处理时,随机将该信号的动作复位为默认值。
- 中断的系统调用
如果一个进程在执行一个低速系统调用而阻塞期间捕捉到的一个信号,则系统调用就被中断不再继续执行。
中断意味着应当唤醒阻塞的系统调用的好机会。
- 低速系统调用,可能会使进程永远阻塞的一类系统调用。
- 在读某些类型的文件(管道、终端设备以及网络设备),如果数据不存在可能永远阻塞
- 在写这些类型的文件时,如果不能立即接受这些数据,则也可能会使调用者永远阻塞
- 打开某些类型文件,在某种条件发生之前也可能会使调用者阻塞(打开终端设备,需要等待直到所连接的调制解调器应答了电话)
- pause(它使调用进程休眠直到捕捉到一个信号)和wait函数
- 某些ioctl操作
- 某些进程间通信函数
硬盘不输入阻塞的低速系统调用,他有请求队列
4.2BSD引入了被中断系统调用的自动重启动。
引入自动重启的理由:用户不知道所使用的输入、输出设备是否低速设备。如果可以用交互式运行,则可能是读、写低速设备。
- 可重入函数
进程捕捉到信号并对其进行处理时,进程正在执行的指令序列被信号处理程序临时中断,他首先执行该信号的处理程序中的命令。处理程序返回时不能判断捕捉到信号时在何处执行。
比如malloc或者getpwnam就会对进程造成破坏。
用到时可以查找哪些程序是可重入的。
- SIGCLD语义
SIGCLD和SIGCHLD两个信号容易混淆。
系统V处理SIGCLD信号的方式不同于其他信号。
如果用signal或sigset(早期设置信号配置的与SRV3兼容)设置信号配置。
SGICLD的早期处理
如果进程特地的设置该信号的配置为SIG_IGN,这调用进程的子进程将不产生僵死进程。注意:与默认动作的“忽略”不同。代之以在子进程终止时,将状态丢掉。
2、如果将SIGCLD的配置设置为捕捉,则内核立即检查是否有子进程准备好被等待,如果这样,则调用SIGCLD处理程序。改变了为此信号编写处理程序的方法。
- 可靠信号术语和语义
产生信号时,内核通常在进程表中设置一个某种形式的标志。
当对信号采取了这种动作,我们说进程递送了一个信号。
在信号产生和递送的时间间隔,称信号时未决的。
进程可以选用信号递送阻塞。如果产生了一个选择阻塞的信号,而且动作时系统默认或者捕捉信号,则为该进程将对此信号保持为未决状态。
每个进程都有一个信号屏蔽字。sigprocmask来检测或更改其当前信号屏蔽字。sigset_t
- kill和raise函数
#include <signal.h>
int kill( pid_t pid, int signo );
int raise( int signo );
kill:
pid > 0 将该信号发送给ID为pid的进程
pid == 0 将该信号发送给同一进程组的所有进程,而且发送进程具有向这些进程发送信号的权限
pid < 0 将该信号发送给其进程组ID等于pid的绝对值,发送进程需具有权限。
pid == -1 将该信号发送给发送进程有权限发送的所有进程。
权限:1、超级用户
2、发送者的实际或者有效用户ID必须等于接收者的实际或者有效用户ID。
编号为0 的信号,称为空信号,kill向一个不存在的进程发送空信号,则返回-1,error设置为ESRCH。(并不发送,执行正常的错误检测),不是原子操作,没有多大价值。