Linux系统编程:进程信号的保存和阻塞

news2024/9/23 17:23:50

目录

一. 信号保存和阻塞的相关概念

二. 进程信号的表示

2.1 进程信号在内核中的表示

2.2 sigset_t 类型

三. 信号集操作相关函数

3.1 sigset_t 类型参数设置相关函数

3.2 sigprocmask 函数

3.3 sigpending 函数

四. 演示代码

4.1 将所有信号的处理方式都注册为不退出进程

4.2 显示未决信号集

4.3 所有信号设置为阻塞

五. 总结


一. 信号保存和阻塞的相关概念

  • 信号递达:进程实际执行信号处理的动作叫做信号的递达(Delivery)
  • 信号未决:信号从实际产生到递达之间的状态,称为信号未决(Pending)
  • 进程可以阻塞某个信号(block),即:即使产生了某个信号也不执行相应的处理动作。
  • 被阻塞信号在产生后,只要不接触阻塞状态,就一直不会递达。
  • 信号阻塞和忽略不是一个概念,信号阻塞是不对进行处理,信号处于未决状态,而忽略信号是指进程接收到了信号,信号也会递达,只是没有实质性的处理动作。

二. 进程信号的表示

2.1 进程信号在内核中的表示

每一个进程的PCB中,都会存储三张用于表示信号状态的表(如图2.1所示),他们分别为block、pending和handler,其中:

  • block:阻塞状态表,其底层实现是位图,如果设置某个进程信号阻塞,block中对应的二进制位就由0变1,block标志位如果为1,信号就不能够递达。
  • pending:未决信号,底层实现也是位图,如果OS检测到了信号,但还没有对信号进行处理,那么在其pending位图中的对应bit位就会被设置为1,如果信号递达,pending位图就会由1变0,如果其block中的二进制位也为1,那么信号就不会递达,pending中的二进制位永远都会为1,直到阻塞状态解除信号递达。
  • handler:是一个函数指针数组,指向其对应的信号处理方法的函数指针,图中SIG_DEF和SIG_IGN分别对应默认处理方式和忽略信号。
  • SIG_DFL和SIG_IGN在源码中的定义为:#define SIG_DEF ((__sighandler_t ) 0) 和 #define SIG_IGN ((__sighandler_t ) 0),进程收到信号,在递达是判断用哪个函数对信号进行处理的流程大概为:先通过 if 和 else if 判断是否选取默认处理方法或者忽略,如果都不是,则走到else执行用于自定义的处理方法。
图2.1 进程信号在内核中的表示

2.2 sigset_t 类型

OS不允许用于直接对block和pending位图中的二进制位进行修改,因此提供了一个OS类型sigset专门用于设置用于进程信号表示的相关位图,sigset_t 可以被称为信号集,sigset_t 的底层是位图结果,用 0/1 来表示状态。

阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask)。

三. 信号集操作相关函数

3.1 sigset_t 类型参数设置相关函数

  • int sigemptyset(sigset_t *set) -- 让set中不包含任何信号,即全部二进制位都是0。
  • int sigfillset(sigset_t *set) -- 让set包含全部信号,即让全部二进制位都是1。
  • int sigaddset(sigset_t *set, int signo) -- 将set指定信号的状态设置为有效1。
  • int sigdelset(sigset_t *set, int signo) -- 删除set指定信号的状态无效0。
  • int sigismember(sigset_t *set, int signo) -- 判断set中某个信号的状态是否为有效1,如果有效,函数返回1,无效返回0。

上面的这些函数,都是成功执行返回0,失败返回-1。 

3.2 sigprocmask 函数

sigprocmask函数 -- 读取或更改信号的阻塞状态

函数原型:int sigprocmask( int how,  sigset_t *set, sigset_t *obset )

函数参数:

        how -- 方法选择,添加阻塞、删除阻塞或设置阻塞

        set -- 如果选择设置阻塞,则用set中的信息设置

        obset -- 输出型参数,读取原来的阻塞信息

返回值:成功返回0,失败返回-1

在sigprocmask函数中,参数 how 用于方法的选择,how有三个可选项,分别为:SIG_BLOCK、SIG_UNBLOCK 和 SIG_SETMASK。

表3.1 sigprocmask函数参数how的选择及对应功能
how功能
SIG_BLOCK添加对特定信号的阻塞状态,相当于mask | set
SIG_UNBLOCK取消对特定信号的阻塞状态,相当于mask & ~set
SIG_SETMASK使用set设置信号阻塞状态,相当于mask = set

3.3 sigpending 函数

sigpending函数 -- 获取进程当前的未决信号集

函数原型:int sigpending(sigset_t *set)

函数参数:set为输出型参数,用于获取当前进程的未决信号集

返回值:运行成功返回0,失败返回-1

四. 演示代码

4.1 将所有信号的处理方式都注册为不退出进程

如果我们在程序中将所有信号的处理方法都设置为不退出,并在之后执行死循环,那么,是不是进程就无法被杀死了呢?

答案显然是否定的,OS的设计者早就考虑到了这一点,9号信号SIGKILL为管理员权限信号,用户不可以重新注册其处理方法,代码和运行结果如下。

代码4.1:所有信号重新注册方法为不退出

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

// 用户自定义的信号处理函数
void siganl_handler(int sig)
{
    std::cout << "recieve a signal, signum:" << sig << std::endl;
}

int main()
{
    std::cout << "this is a process, pid:" << getpid() << ", ppid:" << getppid() << std::endl;

    // 1. 将全部普通信号处理函数重新注册
    for(int sig = 1; sig <= 31; ++sig)
    {
        signal(sig, siganl_handler);
    }

    // 2. 执行死循环
    while(true) 
    { }

    return 0;
}
图4.1 代码4.1的运行结果

4.2 显示未决信号集

代码4.2通过sigprocmask函数,在第5s设置2号SIGINT信号为阻塞状态,,在第8s通过raise函数向进程发送2号SIGINT信号,在第15s取消SIGINT信号的阻塞状态,并实时输出未决状态信息,代码运行结果如图4.2,2号信号的阻塞状态由0至1,并在阻塞状态取消后,执行用户自定义的2号SIGINT信号处理函数,退出进程。

代码4.2:未决信号集的打印

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

// 用户自定义的信号处理函数
void siganl_handler(int sig)
{
    std::cout << "recieve a signal, signum:" << sig << std::endl;
    exit(0);
}

// 未决状态打印函数
void showPending(const sigset_t& pending)
{
    for(int sig = 1; sig <= 31; ++sig)
    {
        if(sigismember(&pending, sig)) std::cout << 1;
        else std::cout << 0;
    }    
    std::cout << std::endl;
}

int main()
{
    // 重新注册2号信号的处理方法
    signal(SIGINT, siganl_handler);

    // bset用于设置阻塞状态,obset用于接收原阻塞状态
    sigset_t bset, obset; 
    // pending用于获取阻塞集
    sigset_t pending;  

    //对信号集设置初始状态
    sigemptyset(&bset);
    sigaddset(&bset, SIGINT);

    int count = 0;
    while(true)
    {
        sleep(1);
        std::cout << ++count << ": " << std::flush;
        
        sigpending(&pending);   // 接收阻塞状态
        showPending(pending);   // 阻塞状态打印

        if(count == 5)
        {
            std::cout << "设置SIGINT信号处于阻塞状态 ..." << std::endl; 
            sigprocmask(SIG_BLOCK, &bset, &obset);
        }

        if(count == 8)
        {
            std::cout << "发送SIGINT信号" << std::endl;
            raise(SIGINT);
        }

        if(count == 15)
        {
            std::cout << "取消对SIGINT信号的阻塞" << std::endl;
            sigprocmask(SIG_SETMASK, &obset, nullptr);
        }
    }

    return 0;
}
图4.2 代码4.2的运行结果

4.3 所有信号设置为阻塞

设置所有信号阻塞,然后死循环,是不是发送任何信号都无法终止进程的运行?

答案当然也不是,9号SIGKILL信号和19号SIGSTOP信号都无法被阻塞,在19号信号使进程终止运行期间,18号SIGCONT信号也无法被阻塞。

代码4.3:验证9号SIGKILL信号无法被阻塞

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

// 未决状态打印函数
void showPending(const sigset_t& pending)
{
    for(int sig = 1; sig <= 31; ++sig)
    {
        if(sigismember(&pending, sig)) std::cout << 1;
        else std::cout << 0;
    }    
    std::cout << std::endl;
}

int main()
{
    // 设置所有信号阻塞
    sigset_t bset, obset;
    sigset_t pending;
    sigfillset(&bset);

    sigprocmask(SIG_BLOCK, &bset, &obset);

    // 依次向进程发送1 - 31号信号,如何输出未决状态
    for(int sig = 1; sig <= 31; ++sig)
    {   
        std::cout << "kill -" << sig << std::endl;
        raise(sig);

        // 获取阻塞信号集并打印
        sigpending(&pending);
        showPending(pending);

        sleep(1);
    }

    return 0;
}
图4.3 代码4.3的运行结果

五. 总结

  • 进程处理信号的动作叫做信号递达、信号从产生到递达的状态叫做信号未决、被阻塞的信号无法递达。
  • 进程PCB中有三张表:block、pending、handler,block和pending为阻塞和未决信号集,handler为指向信号处理函数的函数指针数组。
  • Linux操作系统提供了内置信号集类型sigset_t,用于信号状态的设置,sigprocmask 函数用于读取或设置阻塞信号集,sigpending用于获取阻塞信号集。
  • 9号SIGKILL信号具有管理员权限,用户无法自定义其处理动作,也无法被阻塞。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/908516.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

详解Spring的循环依赖问题、三级缓存解决方案源码分析

0、基础&#xff1a;Bean的生命周期 在Spring中&#xff0c;由于IOC的控制反转&#xff0c;创建对象不再是简单的new出来&#xff0c;而是交给Spring去创建&#xff0c;会经历一系列Bean的生命周期才创建出相应的对象。而循环依赖问题也是由Bean的生命周期过程导致的问题&#…

wustojc3010快速求和

#include <stdio.h> int main() {int n;double s;s0;scanf("%d",&n);for(int i1;i<n;i){ss(double)1.0/(i*(i1.0));//强转一下类型}printf("%.5lf",s);return 0;}

Docker私有仓库创建

1.Docker私有仓库搭建 拉取私有仓库镜像并启动私有仓库容器。 访问私有仓库容器&#xff0c;表明私有仓库搭建成功。 此时私有仓库就已经搭建完成了。 2.将本机的镜像传到私有仓库 3.将私有仓库镜像拉取到本地

论文导读|European Journal of Operational Research近期文章精选:旅行商问题专题

推文作者&#xff1a;王松阁 编者按 在“European Journal of Operational Research近期论文精选”中&#xff0c;我们有主题、有针对性地选择了European Journal of Operational Research中一些有趣的文章&#xff0c;不仅对文章的内容进行了概括与点评&#xff0c;而且也对文…

DHCP协议原理与应用

DHCP协议原理与应用 一、DHCP协议概述1.1、场景描述1.1.1、场景描述11.1.2、场景描述21.1.3、场景描述3 二、DHCP协议工作原理2.1、DHCP简介2.2、DHCP协议名词解释2.3、DHCP服务器配置2.4、PC的DHCP设置2.5、DHCP协议工作过程2.6、DHCP协议报文及用途2.7、DHCP报文介绍2.7.1、D…

面试之快速学习STL-迭代适配器

先放一张大图 参考&#xff1a;http://c.biancheng.net/view/7255.html 1. 反向迭代器 例子&#xff1a; std::list<int> values{1,2,3,4,5};auto start_it values.rbegin();const auto end_it values.rend();//start_it end_it std::reverse_iterator<std::lis…

HCIP 三层架构实验

三层架构实验 拓扑和思路拓扑思路LSW配置LSW1LSW2LSW3 DHCPLSW2LSW1 ACL外网冗余 拓扑和思路 拓扑 思路 首先划分网段&#xff0c;然后LSW1和LSW2和R1可以用ospf宣告就行&#xff0c;然后R1写条缺省指向R2 然后可以将LSW1和LSW2三合一&#xff0c;给交换机配置换分组&#x…

用电脑软件0代码设计WS2812显示效果(含软件下载地址)

用电脑软件设计WS2812显示效果 ws2812显示效果设计软件和单片机程序文件 单片机型号为8脚的STC8G1K08A或STC8G1K17A或者16脚的STC8G1K08或STC8G1K17 烧录时晶振选择22.1184M 百度网盘下载地址&#xff1a;链接: https://pan.baidu.com/s/1cVvA604IKtZ-cIqTX8Jgzw?pwd1234 提取…

数学分析:体形式

确实&#xff0c;面积应该是没有正负的&#xff0c;或者说和曲面的定向应该是无关的。我们用微分形式的积分定义了具有参数形式的曲面的面积。所以这个意思就是说&#xff0c;对于不同的曲面的定向&#xff0c;微分形式应该也不同。 这就是体形式的具体样子&#xff0c;得到每…

中科大 Epc 综合英语经验贴

免修规定考试形式1. 听力&#xff08;813131320分&#xff09;2. 单词&#xff08;20120分&#xff09;3. 语法结构&#xff08;10110分&#xff09;4. 阅读&#xff08;5篇&#xff0c;每题两分&#xff0c;52550分&#xff09; 机考答题建议 免修规定 研究生英语课免修规定&…

已解决Gradle错误:“Unable to load class ‘org.gradle.api.plugins.MavenPlugin‘”

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

<指针进阶>指针数组和数组指针傻傻分不清?

✨Blog&#xff1a;&#x1f970;不会敲代码的小张:)&#x1f970; &#x1f251;推荐专栏&#xff1a;C语言&#x1f92a;、Cpp&#x1f636;‍&#x1f32b;️、数据结构初阶&#x1f480; &#x1f4bd;座右铭&#xff1a;“記住&#xff0c;每一天都是一個新的開始&#x1…

数学建模大全及优缺点解读

分类模型 1、距离聚类&#xff08;系统聚类&#xff09;&#xff08;常用&#xff0c;需掌握&#xff09; 优点&#xff1a; ①将一批样本数据按照他们在性质上的亲密程度在没有先验知识的情况下自动进行分类 ②是一种探索性的分析方法&#xff0c;分类结果不一定相同 例如&am…

Stable Diffusion训练Lora模型

以下内容参考:https://www.bilibili.com/video/BV1Qk4y1E7nv/?spm_id_from333.337.search-card.all.click&vd_source3969f30b089463e19db0cc5e8fe4583a 1、训练Lora的2个重点步骤 第一步&#xff0c;准备训练要使用的图片&#xff0c;即优质的图片 第二部&#xff0c;为…

6.物联网LWIP之并发服务器编程

一。并发服务器&#xff08;多线程&#xff09;实现 #include "socket_udp_server.h" #include "socket_tcp_server.h" #include "socket_wrap.h" #include "ctype.h"static char ReadBuff[BUFF_SIZE];/*** brief udp 服务器任务* p…

深度学习论文: Learning Transferable Visual Models From Natural Language Supervision

深度学习论文: Learning Transferable Visual Models From Natural Language Supervision Learning Transferable Visual Models From Natural Language Supervision PDF: https://arxiv.org/pdf/2103.00020.pdf 官方代码: https://github.com/OpenAI/CLIP PyTorch代码: https:…

vector(介绍)

目录 1.vector的介绍及使用 1.1 vector的介绍 1.2 vector的使用 1.2.1 vector的定义 1.2.2 vector iterator 的使用 1.2.3 vector 空间增长问题 1.2.4 vector 增删查改 1.2.5 vector 迭代器失效问题。&#xff08;重点&#xff09; 2.vector深度剖析及模拟实现 2.1 使用…

PHP“牵手”淘宝商品评论数据采集方法,淘宝API接口申请指南

淘宝天猫商品评论数据接口 API 是开放平台提供的一种 API 接口&#xff0c;它可以帮助开发者获取商品的详细信息&#xff0c;包括商品的标题、描述、图片等信息。在电商平台的开发中&#xff0c;详情接口API是非常常用的 API&#xff0c;因此本文将详细介绍详情接口 API 的使用…

深入理解Semaphore

Semaphore&#xff08;信号量&#xff09;是操作系统中PV操作的原语在java中的实现&#xff0c;它也是基于AQS实现的。其中PV操作是操作系统中一种实现进程互斥与同步的有效方法。PV操作与信号量&#xff08;S&#xff09;的处理有关&#xff0c;P表示通过&#xff0c;V表示释放…

2023.8 - java - 泛型

泛型问题的引出&#xff1a; jdk 1.5 引出泛型 // package 泛型; public class index {public static void main (String[] args){test t new test();t.setContent("aaa");int a (int) t.getContent();System.out.println(a);} }class test{Object content;publi…