【lesson50】信号之信号保存

信号其他相关常见概念

实际执行信号的处理动作称为信号递达(Delivery)
信号从产生到递达之间的状态,称为信号未决(Pending)
进程可以选择阻塞 (Block )某个信号。
被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

在内核中的表示

在这里插入图片描述
在这里插入图片描述
1:该信号被阻塞 0:该信号未被阻塞
在这里插入图片描述

每个信号都有两个标志位分别表示阻塞位图表(block)未决位图表(pending),还有一个函数指针位图表(handler)表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。

SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。

SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。 如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号

一个信号被处理的流程:
在这里插入图片描述
简化:pending ----> block ------>handler
从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。下一节将详细介绍信号集的各种操作。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。

信号保存

sigset_t:基本上语言会给我们提供.h、.hpp且语言自定义类型,而sigset_t是OS提供的类型sigset_t是位图结构但是不允许用户自己进行位操作,对应的操作位图的方法OS会提供接口。
sigset_t---->user是可以直接使用该类型和内置类型 && 自定义类型没有任何差别
sigset_t---->一定需要对应的系统接口来完成对应的功能,其中系统接口需要的参数,可能就包含了sigset_t定义的变量或者对象。
见一见系统接口:
在这里插入图片描述
参数解释
set:输出型参数,可以获取当前进程的pending信号集
在这里插入图片描述
set:传入要修改的目的信号集
oset:输出型参数,获取当前进程旧的信号集
如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。
如果set是非空指针,则 更改进程的信号屏蔽字,参数how指示如何更改。
如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。
how:
在这里插入图片描述
在这里插入图片描述

编码:
1.如果我们对所有的信号都进行了自定义捕捉----->我们是不是就写了一个不会被异常或者用户杀掉的进程
肯定不是的!
测试:
代码:
在这里插入图片描述
测试运行:
创建两个窗口:
窗口1:运行代码
在这里插入图片描述
窗口2:发送信号
在这里插入图片描述
那么我们能用信号杀掉进程吗?可以OS考虑到了这种情况,所以提供了9号信号,9号信号不可以被捕捉。
窗口2:发送9号信号
在这里插入图片描述
窗口:代码被终止
在这里插入图片描述

2.如果我们将2号信号,block并且不断的获取当前的pending信号集,如果我们突然发送一个2号信号,我们应该就可以肉眼看到pending信号集,有一个bit位由0----->1的过程。
代码:

#include <iostream>
#include <signal.h>
#include <unistd.h>

void showpending(sigset_t &pending)
{
    for (int sig = 1; sig <= 31; sig++)
    {
        if (sigismember(&pending, sig) == 1)
        {
            std::cout << "1";
        }
        else
        {
            std::cout << "0";
        }
    }
    std::cout << std::endl;
}

int main()
{
    // 0.捕捉2号信号
    signal(SIGINT, SIG_DFL);

    // 1.定义信号集对象
    sigset_t bset;
    sigset_t obset;
    sigset_t pending;

    // 2.初始化信号集
    sigemptyset(&bset);
    sigemptyset(&obset);

    // 3.添加要进行屏蔽的信号
    sigaddset(&bset, 2);

    // 4.设置set到内核对应的进程内部(默认情况进程不会对任何信号进行block)
    sigprocmask(SIG_BLOCK, &bset, &obset);

    std::cout << "block 2 号信号成功...., pid: " << getpid() << std::endl;
    // 5.重复打印当前进程的pending信号集
    while (true)
    {
        sigpending(&pending);
        showpending(pending);
        sleep(1);
    }

    return 0;
}

运行代码:
在这里插入图片描述
我们确实看到了这个过程。

下面我们看block解除后的效果。
代码:

#include <iostream>
#include <signal.h>
#include <unistd.h>

void showpending(sigset_t &pending)
{
    for (int sig = 1; sig <= 31; sig++)
    {
        if (sigismember(&pending, sig) == 1)
        {
            std::cout << "1";
        }
        else
        {
            std::cout << "0";
        }
    }
    std::cout << std::endl;
}

int main()
{
    // 0.捕捉2号信号
    signal(SIGINT, SIG_DFL);

    // 1.定义信号集对象
    sigset_t bset;
    sigset_t obset;
    sigset_t pending;

    // 2.初始化信号集
    sigemptyset(&bset);
    sigemptyset(&obset);

    // 3.添加要进行屏蔽的信号
    sigaddset(&bset, 2);

    // 4.设置set到内核对应的进程内部(默认情况进程不会对任何信号进行block)
    sigprocmask(SIG_BLOCK, &bset, &obset);

    std::cout << "block 2 号信号成功...., pid: " << getpid() << std::endl;
    // 5.重复打印当前进程的pending信号集
    int count = 0;
    while (true)
    {
        sigpending(&pending);
        showpending(pending);
        sleep(1);

        if (count == 20)
        {
            std::cout << "解除对于2号信号的block" << std::endl;
            int n = sigprocmask(SIG_SETMASK, &obset, nullptr);
            if(n == -1)
            {
                perror("sigprocmask");
            }
        }

        count++;
    }

    return 0;
}

运行结果
在这里插入图片描述
貌似没有一个接口用来设置pending位图表(因为所有的信号发送方式,都是修改pending位图的过程,这些过程由OS操作和维护)。

3.如果我们对进程的所有信号都进行block,那么我们是不是就写了一个不会被异常或者用户杀掉的进程
我们之前知道9号信号不会被阻塞,那么9号信号会被屏蔽吗?
代码:

#include <iostream>
#include <signal.h>
#include <unistd.h>

void showpending(sigset_t &pending)
{
    for (int sig = 1; sig <= 31; sig++)
    {
        if (sigismember(&pending, sig) == 1)
        {
            std::cout << "1";
        }
        else
        {
            std::cout << "0";
        }
    }
    std::cout << std::endl;
}

int main()
{
    sigset_t bset;
    sigset_t obset;
    sigset_t pending;
	sigemptyset(&bset);
    sigemptyset(&obset);
    sigemptyset(&pending);
    
    for(int sig = 1;sig <= 31;sig++)
    {
        sigaddset(&bset, sig);
    }
    
    sigprocmask(SIG_BLOCK, &bset, &obset);

    while(true)
    {
    	sigpending(&pending);
        showpending(pending);
        sleep(1);
    }
    return 0;
}

打开两个窗口:
窗口1运行代码:
在这里插入图片描述

窗口2发送kill指令:

写个发送1-31的kill bash指令

#!/bin/bash

i=1
id=$(pidof mysignal)
while [ $i -le 31 ]
do
    kill -$i $id
    echo "kill -$i $id"
    let i++
    sleep 1
done

开始测试
在这里插入图片描述
我们看到遇到9号指令就停止了
更改bash跳过9号指令

#!/bin/bash

i=1
id=$(pidof mysignal)
while [ $i -le 31 ]
do
    if [ $i -eq 9 ];then
        let i++
        continue
    fi
    kill -$i $id
    echo "kill -$i $id"
    let i++
    sleep 1
done

测试:
在这里插入图片描述
我们看到遇到19号信号进程就被停止。
我们再更改bash屏蔽19号信号,看还有没有不会被block的信号。

#!/bin/bash

i=1
id=$(pidof mysignal)
while [ $i -le 31 ]
do
    if [ $i -eq 9 ];then
        let i++
        continue
    fi
    if [ $i -eq 19 ];then
        let i++
        continue
    fi
    kill -$i $id
    echo "kill -$i $id"
    let i++
    sleep 1
done

在这里插入图片描述
最后还有发现一个现象,20号信号被忽略了

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值