linux信号机制[二]

时间:2024-02-15 19:37:54

阻塞信号

信号相关概念

  • 实际执行信号的处理动作称为信号递达(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。

!!信号捕捉没有创建新的线程和进程。