• Linux-信号2



    前言

    上节课我们学习了信号产生到处理过程的现象以及信号的捕捉,这节课主要学习信号的保存。
    我们需要熟练使用以下函数

    #include
    int sigemptyset(sigset_t *set);
    int sigfillset(sigset_t *set);
    int sigaddset (sigset_t *set, int signo);
    int sigdelset(sigset_t *set, int signo);
    int sigismember(const sigset_t *set, int signo);
    int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
    int sigpending(sigset_t *set);


    一、信号是如何保存的?

    上节课我们说过,信号是在进程的PCB中存储 信号的位图,而实际上,可不止有一个表(位图)。
    在这里插入图片描述

    block信号集是用以保存被block阻塞的信号,一般被称为信号屏蔽字(阻塞信号集),pending信号集就是我们上节课所说的那个收到信号即相对位由0置1的位图,一般被称为未决信号集

    对于阻塞,这是提前预设好的,目的是为了让该进程屏蔽该信号,所以如果一个信号被阻塞,而又收到了该信号,虽然pending信号集由0置1,但是不进行delivery(信号递达:信号递达操作即信号的处理)操作。

    而这两个信号集,在我们看来理解其实就是位图,但是OS将他们封装成了自定义类型sigset_t,通过这样的封装,对于我们用户就无法随心所欲地更改其信号集的数据,只能通过OS提供给我的系统接口函数
    现在来认识一下系统交给我们的一些接口函数。

    int sigemptyset(sigset_t *set);

    函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有
    效信号。
    成功返回0,出错返回-1

    int sigfillset(sigset_t *set);

    函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系
    统支持的所有信号。
    成功返回0,出错返回-1

    int sigaddset (sigset_t *set, int signo);

    函数sigaddset用于将set所指向的信号集的signo信号置位。
    成功返回0,出错返回-1

    int sigdelset(sigset_t *set, int signo);

    函数sigdelset用于将set所指向的信号集的signo信号置零。
    成功返回0,出错返回-1

    int sigismember(const sigset_t *set, int signo);

    函数sigismember用于判断set所指向信号集的signo信号是否为1。
    是则返回1,否则为0。

    int sigpending(sigset_t *set);

    函数sigpending用于读取当前进程的未决信号集,通过set参数传出。
    调用成功则返回0,出错则返回-1。

    int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

    函数sigprocmask用于更改block信号集,参数how为选项,该函数提供三种选项

    1.SIG_BLOCK

    该选项是希望在目前的信号屏蔽字添加set信号集的有效信号,相当于mask=mask|set

    2.SIG_UNBLOCK

    该选项是希望在目前的信号屏蔽字去除set信号集的有效信号,相当于mask=mask&(~set)

    3.SIG_SETMASK

    该选项就比较暴力些,相当于mask=set

    而参数oset为输出型参数,用于保存老信号集。

    需要注意的,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的
    状态。

    二、学习步骤

    我们提出三个问题并对问题进行一一解答来进行我们的学习

    问题1. 如果我们将一个死循环进程的所有信号都捕捉,是不是该进程无法被退出?

    问题2. 如果我们将一个信号block阻塞,并发送对应信号,pending表的对应该信号bit位是不是由0置1?

    问题3. 如果我们将一个死循环进程的所有信号都阻塞,是不是该进程也无法被退出?

    1.解答问题1

    代码如下(示例):

    #include
    #include
    #include
    #include
    
    void catchSig(int signum)
    {
        std::cout << "pid " << getpid() <<" :捕捉到信号 " << signum << std::endl; 
    }
    
    int main()
    {
        for(int i = 1; i <= 31 ; i++)
        {
            signal(i,catchSig);
        }
    
        while(1)
        {
            std::cout << "pid " << getpid() << " :进程运行中... " <<std::endl;
            sleep(1);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这个程序会在运行时捕捉1-31号的所有信号,那么是不是这个进程就无法退出了呢?
    我们通过命令行依次输入kill命令来进行对该进程发送信号,发现在发送9号信号的时候,进程还是被终止了,说明进程的9号信号没有被捕捉!
    在这里插入图片描述
    所以这里的结论就是如果我们将一个死循环进程的所有信号都捕捉,该进程仍然可以通过9号信号退出,因为设计OS的人知道,如果有恶意程序真的将所有信号都可以捕捉,那么后果是十分严重的!

    2.解答问题2

    代码如下(示例):

    #include 
    #include 
    #include 
    #include 
    #include 
    
    void ShowSet(sigset_t &set)
    {
        for (int i = 1; i <= 31; i++)
        {
            // 通过sigismember来打印我们的pending信号集
            std::cout << sigismember(&set, i);
        }
        std::cout << std::endl;
    }
    
    int main()
    {
        sigset_t set;
        sigset_t block;
        sigset_t oset;
        // 1.进行初始化
        sigemptyset(&set);
        sigemptyset(&block);
        sigemptyset(&oset);
    
        // 2.阻塞2号信号
        sigaddset(&block, 2);
        sigprocmask(SIG_BLOCK, &block, &oset);
    
        std::cout << "pid " << getpid() << std::endl;
        // 3.循环打印未决信号集
        while (1)
        {
            // 3.1 获取当前的未决信号集
            sigpending(&set);
            ShowSet(set);
            sleep(1);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在这里插入图片描述
    我们很清楚的看到第二位由0置1了。
    所以结论就是如果我们将一个信号block阻塞,并发送对应信号,pending表的对应该信号bit位是由0置1。

    3.解答问题3

    代码如下(示例):

    #include 
    #include 
    #include 
    #include 
    #include 
    
    void ShowSet(sigset_t &set)
    {
        for (int i = 1; i <= 31; i++)
        {
            // 通过sigismember来打印我们的pending信号集
            std::cout << sigismember(&set, i);
        }
        std::cout << std::endl;
    }
    
    void SendSig(int signum)
    {
        if ((signum != 19) &&(signum != 9) && (signum <= 31))
            raise(signum);
    }
    
    int main()
    {
        sigset_t set;
        sigset_t block;
        sigset_t oset;
        // 1.进行初始化
        sigemptyset(&set);
        sigemptyset(&block);
        sigemptyset(&oset);
    
        // 2.阻塞所有信号
        for (int i = 1; i <= 31; i++)
        {
            sigaddset(&block, i);
        }
        sigprocmask(SIG_SETMASK, &block, &oset);
    
        // 3.循环打印未决信号集
        int count = 1;
        while (1)
        {
            // 3.1 获取当前的未决信号集
            sigpending(&set);
            ShowSet(set);
            SendSig(count++);
            sleep(1);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    在这里插入图片描述
    我们看到这样的情况,可以得出看出9号信号和19号信号是不可屏蔽的!

    所以这里的结论就是如果我们将一个死循环进程的所有信号都屏蔽,该进程仍然可以通过9号信号退出。


  • 相关阅读:
    通达信软件L2接口的委托队列有什么用?
    消息队列解决的问题
    最快速搭建个人服务器图床siuuuuu
    【牛客刷题--SQL篇】多表查询链接查询 SQL22统计每个学校的答过题的用户的平均答题数
    软件开发 23个设计模式收集
    Postgresql源码(77)plpgsql中参数传递和赋值
    基于神经网络多项式插值的图像超分辨重构研究-附Matlab代码
    Python读写文件操作
    LeetCode 面试题 10.05. 稀疏数组搜索
    【C++基础】类与对象(上):访问限定符、类作用域、类实例化、类对象模型、this指针
  • 原文地址:https://blog.csdn.net/fengjunziya/article/details/136357668