文章目录
- 信号的基本概念
- 信号保存
- block位图
- pending位图
- handler数组
- 信号处理
- sigset_t
- sigemptyset
- sigfillset
- sigaddset sigdelset
- sigismember
- sigprocmask
- 捕捉信号
信号的基本概念
- 信号递达:实际处理信号的动作
- 信号未决:信号从产生到递达之间的状态
- 信号阻塞:不会被递达的信号
- 信号忽略:递达的动作是忽略
阻塞和忽略的区别
阻塞指的是这个信号不会被递达,也就是不对其进行操作处理
忽略指的是这个信号可以递达,只是处理的动作是忽略
信号保存
在进程的PCB中有如下三个数据结构和信号相关
前两个是位图,后一个是数组
block位图
这个位图表示哪些信号被阻塞,0或1表示是否被阻塞,某一个位置表示对应的信号
pending位图
这个位图用来存储收到的信号,0或1表示是否收到,某一个位置表示对应的信号,这个位图也称之为信号集,也就是未决的情况
handler数组
这个数组是一个函数指针数组,里面的内容是函数指针,下标表示收到n号信号,调用的处理方法就是对应的函数指针
SIG_DFL宏代表这个函数是默认处理函数
SIG_IGN宏代表收到这个信号后,进行忽略这个信号
信号处理
这个函数可以手动更改handler数组,让进程在捕捉到对应信号的时候调用我们指定的函数处理
#include<iostream>
#include<signal.h>
using namespace std;
void func1(int signum)
{
cout<<"进程捕捉到"<<signum<<"号信号,PID为:"<<getpid()<<endl;
}
int main()
{
signal(SIGINT, func1);
while(1)
{
cout<<"进程PID为:"<<getpid()<<endl;
sleep(1);
}
return 0;
}
sigset_t
这个数据结构的本质是一个位图,其实就上面block位图和pending位图的数据结构
对于这个数据结构也有很多操作
sigemptyset
int sigemptyset(sigset_t *set)
这个函数是初始化set锁指向的信号集,对其置零,让其中不包含任何有效信号
sigfillset
int sigfillset(sigset_t *set)
这个函数是初始化全部置一,让其包含所有信号
sigaddset sigdelset
int sigaddset(sigset_t *set. int signo)
这两个是一对,分别对应添加和删除
需要注意的是,在使用sigset_t之前,一定要调用前面的任意一个初始化函数,让整个信号集处于确定的状态
sigismember
int sigismember(const sigset_t* set, int signo)
这个是用来判断是否有效,有效则返回1,无效返回0
sigprocmask
这个函数可以用于读取或更改阻塞信号集,也成为信号屏蔽字
int sigprocmask(int how, const sigset_t *set, sigset_t *oset)
成功返回0,出错返回-1
这里有几种情况
如果set和oset都不是空指针,这个函数就把原来的信号屏蔽字拷贝到oset中,然后按照how参数的规则进行修改
如果set非空而oset为空指针,这个函数就直接按照how进行修改而不拷贝
如果set为空指针而oset非空,则读取当前进程的信号屏蔽字
假设当前进程的信号屏蔽字是mask,我们期望更改的信号集是set
how的参数和功能如下
- SIG_BLOCK:此时set信号集表示我们想要添加到信号屏蔽字中的信号,原理是使用位运算mask |= set;
- SIG_UNBLOCK:此时set信号集表示我们希望从信号屏蔽字中接触阻塞的信号,使用位运算 mask = mask&~set;
- SIG_SETMASK:此时set信号集表示我们想要变成的样子相当于直接赋值,mask = set;
捕捉信号
先说结论再说原理
当进程从内核态转换为用户态时,会自动进行信号的检测和捕捉处理
一般当代码进行执行的时候,操作系统是处于用户态的,但是执行到系统调用,或者出现异常中断时,操作系统会变成内核态
因为系统调用和异常处理的工作实际上是很底层的代码和函数,只有当操作系统处于内核态时才可以执行,此时这个进程的优先级非常高
最本质里面,其实就是在CPU中的CR3寄存器中,表示当前CPU处于什么状态,1表示内核态,3表示用户态,而这个寄存器对于用户也是不可见的,只由操作系统管理
也就是说当程序执行系统调用时会进入内核态
执行完系统调用时会回到用户态
在状态转换的时候,就进行信号的检测和处理
当这时有信号到来的时候,代码会跳转到信号处理的函数
当信号处理函数返回时还会执行特殊的系统调用,再回到内核态
大概流程如下图