【信号】信号的保存
信号的保存
信号其他相关常见概念
实际执行信号的处理动作称为信号递达(Delivery)
信号从产生到递达之间的状态,称为信号未决(Pending)。
进程可以选择阻塞 (Block )某个信号。
被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
进程在接收到信号之后,可能不处理,那么信号就会保存,等到合适的时候再进行处理,一般这个信号会保存在一张位图里,这个位图是进程PCB 的一部分,比特位的0和1代表信号是否收到,比特位的位置代表第几号信号,所谓保存信号就是修改这张位图的内容,OS是进程的管理者,它才有资格修改这张位图的内容
OS提供了三张表,block表,pending表,handler表
block表:比特位的位置代表第几号信号,内容代表该信号是否被阻塞,例如,第二个比特位的内容是1 ,代表第2号信号被阻塞
pending表:就是信号保存的表,信号没有被处理时就保存在这张表里
handler表:函数指针数组,保存的是函数方法的地址,有3种,SIG_DFL是终止处理,SIG_IGN是忽略处理,最后一个是自定义处理
每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。
sigset_t类型
如果你要修改这些位图的内容,肯定是不行的,你没有权限,OS才有,所以为了让我们更好的访问到这些位图并修改,OS提供了sigset_t类型,它的底层其实就是位图
每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。
sigset_t操作函数
sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做任何解释,比如用printf直接打印sigset_t变量是没有意义的
函数sigemptyset:初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号。
函数sigfillset:初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信号包括系统支持的所有信号。
注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。
初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号
这四个函数都是成功返回0,出错返回-1。
函数sigismember:是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1
sigprocmask函数
调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)
如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。
如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达
sigpending函数
读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。
函数运用例子
思路:
1.对2号信号进行屏蔽
2.重复打印pending表
3.几秒后解除对二号信号的屏蔽
4.运行程序,发送信号,观察现象
代码:
#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;
void Printpending(sigset_t& pending)
{
for(int signo=31;signo>=1;signo--)
{
if(sigismember(&pending,signo)) cout<<'1';
else cout<<'0';
}
cout<<endl;
}
int main()
{
//1.先对2号信号进行屏蔽
//1.1 数据储备
sigset_t bset,oset;// 在哪里开辟的空间???用户栈上的,属于用户区
sigemptyset(&bset);
sigemptyset(&oset);
sigaddset(&bset,2);//我们已经把2号信号屏蔽了吗? 其实没有,并没有设置到系统的block表里
//1.2调用系统调用,把2号信号屏蔽弄进内核里
sigprocmask(SIG_SETMASK,&bset,&oset);
//2.重复打印pending表
sigset_t pending;
int cnt=0;
while(true)
{
//2.1获取pending表
int n=sigpending(&pending);
if(n<0) continue;
//2.3打印pending表
Printpending(pending);
cnt++;
sleep(1);
if(cnt==6)
{
//2.3解除对二号信号的屏蔽
cout << "unblock 2 signo" << endl;
// sigdelset(&bset,2);
// sigprocmask(SIG_SETMASK,&bset,&oset);
//或者这样
sigprocmask(SIG_SETMASK,&oset,nullptr); //oset保存的是上一次的block表
}
}
//3.发送信号
return 0;
}
结果:
注意:不是所以得信号都可以被屏蔽,9和19号信号你无法设置屏蔽,和自定义捕捉一样