信号是软件层面上的异常
(一)Linux信号
可以通过man 7 signal 查看Linux提供的标准信号。其中描述的信号的编号在不同的处理器体系结构的差异有所不同。
(二)信号传送的步骤
传送一个信号到目的进程通常由两个不同的步骤组成:
(1)发送信号
发送信号的原因:
a)内核检测到一个系统事件
b)一个进程调用了kill函数,显式地要求内核发送一个信号给目的进程。
内核通过更新目的进程的上下文的某个状态,发送一个信号给目的进程
发送信号的方式:
a)通过/bin/kill程序发送信号
b)通过键盘发送
常用的与键盘相关的信号:
SIGINT :来自键盘的终端信号(CTRL+C)
SIGQUIT:来自键盘的退出信号(CTRL +\)
c)通过kill函数发送
函数原型如下:
#include<sys/type.h>
#include<signal.h>
int kill(pid_t pid , int sig);
如果pid大于0,则给进程pid 发送信号sig,如果pid小于0,则给abs(pid)进程组中的每个进程发送信号sig。如果pid等于0,则不发送信号
d)通过alarm函数发送
alarm - set an alarm clock for delivery of a signal
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
alarm() arranges for a SIGALRM signal to be delivered to the calling process in seconds seconds.
(2)接收信号
当目的进程被内核强迫以某种方式对信号的发送做出反应时,目的进程就接收了信号。
内核为每个进程在pending位向量中维护着待处理信号的集合,在blocked位向量中维护着被阻塞的信号集合,传送一个类型为k的信号,内核就会设置pending中的k位,接受一个类型为k的信号,内核就会清除pending中的k位。
每个信号类型都有一个预定义的默认行为,是如下当中的一种:
a)进程终止
b)进程终止并转储存储器
c)进程停止直到被SIGCONT信号重启
d)进程忽略该信号
进程可以通过signal函数修改和信号相关联的默认行为,SIGSTOP和SIGKILL两个信号的默认行为是不能修改的。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum的值为:SIG_IGN,则忽略类型为signum的信号
signum的值为:SIG_DFL,则类型为signum的信号行为恢复为默认行为
signum的值为:其他值,则设置类型为signum的信号的行为是用户定义的函数(信号处理程序)。只要进程接收到一个类型为signum信号,就调用这个程序
信号处理问题分析的三大要点
(1)待处理信号被阻塞
假设一个进程捕获了一个类型为k的信号,且当前正在运行它的信号处理程序,则如果另外一个类型为k的信号传递给这个进程,则这个类型为k的信号将变成待处理的,不会被接收,直到处理程序返回。
(2)待处理信号不会排队
任意类型的信号,最多只能有一个待处理的信号,所以当上面描述的已经有一个类型为k的待处理信号的时候,后续的传递到这个进程的待处理信号都将被直接丢弃。
(3)系统调用可以被中断
在某些系统中当处理程序捕捉到一个信号时,被中断的慢速系统调用在信号处理程序返回时将不再继续,而是立即返回一个错误条件,并设置errno为EINTR。