阻塞信号
信号相关概念
- 实际执行信号的处理动作称为信号递达(Delivery)
- 信号从产生到递达之间的状态,称为信号未决(Pending)。[收到信号但是没有处理]
- 进程可以选择阻塞 (Block )某个信号。
- 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
- 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
忽略和阻塞处理的区别:
忽略代表信号已经抵达,并且处理了,处理的方式就是什么都不干。而阻塞则信号压根没有被递达。阻塞的信号如果不解除阻塞的话永远不会被递达,只有解除阻塞才可以。
由上面的概念我们可以得出pcb内部其实由3张表
对padding的修改代表是否收到了信号,以及收到了什么信号。handler 是函数指针,代表着各个信号的实现方法。 自定义信号捕捉,就是把自己的函数处理方式填到handler里面。
在信号处理的时候方法是直接调用的吗?
NO对于我们传入的参数,他会先进行强制类型转换,如果等于1,执行默认行为,等于2忽略,都不等于才会执行调用对应的方法。
block表也是一个位图结构和padding一模一样。唯一的差别是位图中的内容代表信号是否被阻塞。
一个信号被处理的过程
操作系统修改padding位图,然后查找block表看是否被阻塞,然后进入对应的handler表执行处理方法。
sigset_t
每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号 的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有 效”和“无效”的含义是该信号是否处于未决状态。
阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略
基本上语言会给我们提供.h .hpp&&语言类自定义类型,同时os也会给我们提供.h和os自定义类型。sigset_t是一格位图结构但是不允许用户自己进行处理,操作系统提供对应的位操作方法。这个类型用户可以直接使用,和默认类型没有差别。
接口:
sigpending
#include<singal.h>
sigpending(sigset_t *set)
读取当前进程的未决(padding)信号集,通过set参数传出,拿给用户。调用成功则返回0,出错则返回-1。
sigprocmask
#include<singal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
检查并更改阻塞信号集返回值:若成功则为0,若出错则为-1
如果oldset是非空指针,则读取进程的当前信号屏蔽字通过oldset参数传出(输出型参数)。如果set是非空指针,则 更改进程的信 号屏蔽字,参数how指示如何更改。如果oldset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后 根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值:
小实验:
对所有信号进行捕捉——是不是就写了一个不会异常或者被杀掉的进程?
#include<iostream>
#include<signal.h>
//信号捕捉
void catchsig(int sigum)
{
cout<<"捕捉到了信号:"<<sigum<<"pid:"<<"getpid()"<<endl;
}
int main()
{
alarm(1);
int count =0;
while(1)
{
cout<<"cout :"<<count++<<endl;
}
}
我们运行代码会发现一个现象,所有的信号都可以被捕捉,但是9号信号不可以被捕捉自定义。这个就是为了避免这种情况发生 。
如果将2号信号block,并不断获取pending信号机,如果发送一个2号信号我们是不是就应该看到pending中有一个比特位0->1? 是的
#include<iostream>
#include<unistd.h>
#include<cassert>
#include<signal.h>
void catsig(int signal)
{
std::cout<<signal<<"signal信号被捕捉";
}
static void showpening(sigset_t &pending)
{
for(int sig=1;sig<31;sig++)
{
if(sigismember(&pending,sig))//查看是不是在进程中
{
std::cout<<"1"<<std::endl;
}
else
{
std::cout<<"0"<<std::endl;
}
}
std::cout<<std::endl;
}
int main()
{
//1.定义信号集对象
sigset_t set,oset;
sigset_t pending;
//2.初始化信号集
sigemptyset(&set);
sigemptyset(&oset);
sigemptyset(&pending);
//3.添加要屏蔽的信号
sigaddset(&set,2);//
//4.设置set到操作系统内核中【默认不会对任何信号block】
int n=sigprocmask(SIG_BLOCK,&set,&oset);
assert(n==0);
(void)n;
std::cout<<"block 2号信号成功"<<std::endl;
//5.重复打印当前进程的信号集
while(1)
{
//5.1获取当前进程padding集
sigpending(&pending);
//5.2显示pending中没有被递达地信号
showpening(pending);
sleep(1);
}
return 0;
}
如果对所有的进程进行block——是不是就写了一个不会异常或者被杀掉的进程?
不会,9号以及19号信号不可以被捕捉阻塞或者屏蔽。
信号捕获之后可能无法处理,合适地时候是什么时候?流程是什么
合适的时候:信号相关字段在pcb内部(内核状态)。在内核态,返回用户态地时间进行信号地检测和处理。进行系统调用,缺陷陷阱等。int 80 特殊接口 内置在系统调用中。
内存映射页表,用户级页表,每个进程都有一份,不共有,内核级进程操作系统共有一份,通用。
捕捉信号
信号捕捉在处理信号的时候又出现新的信号 ,os如何处理呢?block。
!!信号捕捉没有创建新的线程和进程。