信号的三个阶段
一、阻塞信号
1.信号的其他相关概念
实际执行信号的处理动作称为信号递达(Delivery)。
信号从产生到递达之间的状态,称为信号未决(Pending)。本质是这个信号被暂存在task_struct信号位图中,未决。
进程可以选择阻塞 (Block )某个信号。本质是OS允许进程暂时屏蔽指定的信号。1.该信号依旧是未决的。2.该信号不会被传递,直到解除堵塞方可传递。
被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
2.信号集操作函数
#include <signal.h>
//初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号。
int sigemptyset(sigset_t *set);
//初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信号包括系统支持的所有信号。
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);
//这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。
sigprocmask
修改的是block表
//调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。
//修改的是进程的block位图
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
//set 输入型 oset 输出型
//返回值:若成功则为0,若出错则为-1
sigpending
不对pending做修改,而只是单纯的获取进程的pending。
如果我的进程预先屏蔽2号信号,不断地获取当前进程的Pending位图并不断打印,然后手动发送2号信号,因为2号信号不会被递达,打印的Pending位图有一位会被置1。
#include <signal.h>
sigpending(sigset_t *set);
读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。
二、捕捉信号
1.内核如何实现信号的捕捉
简化模型
2.sigaction
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回 0,出错则返回 - 1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体。
将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动作。
代码演示
#include<stdio.h>
#include<string.h>
#include<signal.h>
#include<unistd.h>
void handler(int signo){
printf("get a signo:%d\n", signo);
}
int main(){
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = handler;
// 本质是修改当前进程的handler函数指针数组的特定内容
sigaction(2, &act, NULL);
while(1){
printf("Hello.\n");
sleep(1);
}
return 0;
}
注:当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理结束为止。如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。