Linux进程信号(2)--信号的保存

目录

1.阻塞信号

1.1 信号其他相关常见概念

1.实际执行信号的处理动作称为信号递达(Delivery)

2.信号从产生到递达之间的状态,称为信号未决(Pending)。

3.进程可以选择阻塞 (Block )某个信号。

1.2信号在内核中的表示

sigset_t

信号集操作函数

使用sigprocmask函数修改block表

使用sigpending函数查看pending表


1.阻塞信号

 在解释信号的保存时,我们要先了解一下信号中的专有名词及其概念。

1.1 信号其他相关常见概念

1.实际执行信号的处理动作称为信号递达(Delivery)

信号递达,通俗来说就是处理信号。

处理信号的三种方式:

1.信号的忽略

2.信号的默认

3.信号的自定义捕捉

信号的默认

这里我们先编写一个程序:

#include<iostream>
#include<unistd.h>
#include<signal.h>int main()
{while(true){std::cout<<"getpid():"<<getpid()<<"    running..."<<std::endl;sleep(1);}return 0;
}

运行起来,然后再使用kill -信号编号 进程pid。给进程发送信号。

这里就是信号9的默认。

信号的自定义

这里我们要使用函数:

sighandler_t signal(int sugnum,sighandler_t handler)

这里signum是你想自定义信号的编号handler一个函数指针,指向的是返回值为void,参数为一个int的函数。(这里也可以用一个宏SIG_DFL恢复信号的默认处理)

作用:收到信号,不按照默认方式,自定义处理信号。

#include<iostream>
#include<unistd.h>
#include<signal.h>void handler(int signal)//自定义处理信号2的内容
{std::cout<<"handler:"<<signal<<std::endl;exit(2);
}int main()
{signal(2,handler);//自定义信号2.while(true){std::cout<<"getpid():"<<getpid()<<"    running..."<<std::endl;sleep(1);}return 0;
}

使用宏SIG_DFL恢复默认处理:

#include<iostream>
#include<unistd.h>
#include<signal.h>void handler(int signal)//自定义处理信号2的内容
{std::cout<<"handler:"<<signal<<std::endl;exit(2);
}int main()
{signal(2,handler);//自定义信号2.signal(2,SIG_DFL);//恢复信号2,默认处理while(true){std::cout<<"getpid():"<<getpid()<<"    running..."<<std::endl;sleep(1);}return 0;
}

信号的忽略

这里还是使用函数signal,但是要使用宏SIG_IGN才能实现函数忽略。

代码:

#include<iostream>
#include<unistd.h>
#include<signal.h>void handler(int signal)//自定义处理信号2的内容
{std::cout<<"handler:"<<signal<<std::endl;exit(2);
}int main()
{signal(2,SIG_IGN);//忽略信号2.while(true){std::cout<<"getpid():"<<getpid()<<"    running..."<<std::endl;sleep(1);}return 0;
}

这里我们可以看到当我们向进程发送信号2时,它并没有退出进程,所以它忽略了信号2.

这里可能大家会有这样的疑问?

signal函数的第二个参数是一个函数指针,为什么可以使用宏SIG_DFL与SIG_ING呢?

这里我们可以直接看这两个宏的定义:

这里我们可以看出,将0强转成了函数指针类型,1也强转成了函数指针类型。

如何看待信号的忽略?

这里忽略,就是处理信号。

2.信号从产生到递达之间的状态,称为信号未决(Pending)

这里我们知道,如果当进程收到信号时,正在执行更为重要的事务,进程并不会立马处理信号,而是先将信号保存下来,等待合适的时机再处理。所以在信号产生,到信号处理(信号递达)这段时间里我们叫做信号未决。

这里我们知道,保存信号是使用位图来保存的,信号未决具体点可以理解为:在信号位图中,就叫做信号未决。

3.进程可以选择阻塞 (Block )某个信号。

信号未决之后,暂时不递达,直到接触信号的阻塞。

举例理解:

在上课前老师留下了放假作业(接受信号,保存),但是因为上课要听老师讲课,我们不能在课堂上写作业(被阻塞),我们只能在放学后才能写作业(接触阻塞)。

注:

1.信号未决不一定阻塞,但收到信号并阻塞,一定未决。

2.没有收到信号,可以设置信号的递达动作。

3.没有收到信号,可以设置信号阻塞,也可以解除阻塞。

4.阻塞与忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

1.2信号在内核中的表示

上面,我们介绍了信号的递达,未决,阻塞。在OS里是这样实现的。

信号在内核中的表示示意图

首先有block,pending,handler三张表,其中:

pending表:是信号未决表,表的下标代表着是几号信号,表的内容代表着是否收到信号。(使用位图的方式记录)

handler表:对应信号的处理方法,里面可以存放函数指针,或者是SIG_DFL/SIG_ING这样的宏。数组的下标代表着信号编号-1。

这里在进程启动时,就形成了这两张表。因此在进程收到信号前,就认识信号(如何处理信号)

block表:信号阻塞表,pending表一样,使用了位图。比特位的位置:表示信号编号。比特位的内容表示,是否对特定的信号进行屏蔽(阻塞)。

对于一个信号的识别,使用这三张表,要横着看

比如以1号信号为列:

当我们收到1号信号时,我们先将pending表中对应的比特位置为1,然后再看block表中对应的比特位的内容,为0,表示不阻塞,在合适的时候,会在handler表中找到对应的方法处理信号。若为1,则表示信号阻塞,不会对信号进行任何处理,等到比特位的内容变为0,会在合适的时候,在handler表中找到对应的方法处理信号。

因此在收到信号时,能不能被处理,首先取决于对因block表中内容。

上面我们知道对于信号的处理取决于PCB中的这三张表,因此如果我们要控制进程对信号的处理,那么就需要改变这三张表的内容,但是对于我们用户来说,我们并不清楚这三张是如何存储。因此操作系统为我们提供了一系列的系统调用函数,来帮助我们。

sigset_t

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

信号集操作函数

#include <signal.h>//将位图里的比特位全部清零
int sigemptyset(sigset_t *set);//对指定的位图全部置1
int sigfillset(sigset_t *set);//将指定信号集中的指定比特位置1
int sigaddset (sigset_t *set, int signo);//将指定信号集中的指定比特位置0
int sigdelset(sigset_t *set, int signo);//判定一个信号是否在信号集中
int sigismember(const sigset_t *set, int signo);

使用sigprocmask函数修改block表

调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset); set为想要修改的信号,
oset记录调用前的block,用于恢复原来的block返回值:若成功则为0,若出错则为-1
如果 oset 是非空指针 , 则读取进程的当前信号屏蔽字通过 oset 参数传出。如果 set 是非空指针 , 则 更改进程的信号屏蔽字, 参数 how 指示如何更改。如果 oset set 都是非空指针 , 则先将原来的信号 屏蔽字备份到 oset , 然后根据set how 参数更改信号屏蔽字。假设当前的信号屏蔽字为 mask, 下表说明了 how 参数的可选值

代码演示:屏蔽2号信号。

#include<iostream>
#include<unistd.h>
#include<signal.h>void handler(int signal)//自定义处理信号2的内容
{std::cout<<"handler:"<<signal<<std::endl;exit(2);
}int main()
{signal(2,handler);sigset_t block,oblock;sigemptyset(&block);//位图置0操作sigemptyset(&oblock);sigaddset(&block,2);//这里并没有对2号信号做屏蔽sigprocmask(SIG_BLOCK,&block,&oblock);//调用系统接口,对2号信号进行屏蔽while(true){sleep(1);std::cout<<"getpid: "<<getpid()<<std::endl;}return 0;
}

这里可能有人会这样问,既然我们可以控制屏蔽信号,那么如果我们把所有信号都屏蔽了,是不是一旦这样的死循环进程运行起来,是不是就不会被杀掉了?

这里我们直接上代码演示: 

#include<iostream>
#include<unistd.h>
#include<signal.h>void handler(int signal)
{std::cout<<"handler:"<<signal<<std::endl;exit(2);
}int main()
{signal(2,handler);sigset_t block,oblock;sigemptyset(&block);//位图置0操作sigemptyset(&oblock);for(int i=1;i<=31;i++)sigaddset(&block,i);//将block位图上的所有比特位全部置为1.sigprocmask(SIG_BLOCK,&block,&oblock);//对所有信号进行屏蔽while(true){sleep(1);std::cout<<"getpid: "<<getpid()<<std::cout<<"  我已经屏蔽掉了所有信号,你来打我啊!"<<std::endl;}return 0;
}

这里我们可以发现当我们对进程发送除9之外的信号,进程并不会停止(信号被屏蔽),而当收到9信号时,进程就直接执行信号9对应的默认动作。(19号信号也没有被屏蔽)

9号信号我们成为管理员信号,它并不会被屏蔽。

使用sigpending函数查看pending表

#include <signal.h>
int sigpending(sigset_t *set)读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。 下面用刚学的几个函数做个实验。程
序如下

这里我们直接上代码:

解释程序目的,不断打印pendling表中比特位的内容。在最开始的前10秒,阻塞信号2。然后将信号2从block表中删除,我们要在10秒前向进程发送2号信号。

#include<iostream>
#include<unistd.h>
#include<signal.h>void handler(int signal)
{std::cout<<"handler:"<<signal<<std::endl;//exit(2);
}void PrintPending(sigset_t& pending)//打印pending中每一个比特位的内容
{for(int signo=21;signo>=1;signo--){if(sigismember(&pending,signo)){std::cout<<'1';}else {std::cout<<'0';}}std::cout<<std::endl;
}int main()
{signal(2,handler);//自定义2号信号//屏蔽2号信号sigset_t block,oblock;sigemptyset(&block);sigemptyset(&oblock);sigaddset(&block,2);sigprocmask(SIG_BLOCK,&block,&oblock);std::cout<<"getpid: "<<getpid()<<std::endl;int cnt=0;//让进程不断获取当前进程的pending表sigset_t pending;while(true){cnt++;sigpending(&pending);//获取pending表中的内容PrintPending(pending);if(cnt==10){std::cout<<"解除对2号信号的屏蔽,2号信号已递达。"<<std::endl;sigprocmask(SIG_SETMASK,&oblock,nullptr);}sleep(1);}return 0;
}

这里我们可以看到一旦信号2,不阻塞,就立即递达了。并且pending表中对应的比特位置0.

这里可能有人会问pending表置0这个操作是在处理信号前,还是后执行的?

这里我们可以直接用代码验证:

#include<iostream>
#include<unistd.h>
#include<signal.h>void PrintPending(sigset_t& pending);
void handler(int signal)
{std::cout<<"#####################"<<std::endl;sigset_t sig;sigpending(&sig);PrintPending(sig);std::cout<<"######################"<<std::endl;std::cout<<"handler:"<<signal<<std::endl;//exit(2);
}void PrintPending(sigset_t& pending)//打印pending中每一个比特位的内容
{for(int signo=21;signo>=1;signo--){if(sigismember(&pending,signo)){std::cout<<'1';}else {std::cout<<'0';}}std::cout<<std::endl;
}int main()
{signal(2,handler);std::cout<<"getpid: "<<getpid()<<std::endl;while(true){std::cout<<"running..."<<std::endl;sleep(1);}return 0;
}

这里我们采用的是,自定义信号2,在处理时,将pending表的内容打印出来,如果全部位0,则在修改pending实在递达前实行的,否则实在递达之后修改的。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nginx slice模块的使用和源码分析

文章目录 1. 为什么需要ngx_http_slice_module2. 配置指令3. 加载模块4. 源码分析4.1 指令分析4.2 模块初始化4.3 slice模块的上下文4.2 $slice_range字段值获取4.3 http header过滤处理4.4 http body过滤处理5 测试和验证 1. 为什么需要ngx_http_slice_module 顾名思义&#…

Vue中keep-alive的作用、原理及应用场景

在进行Vue开发的过程中&#xff0c;我们经常会遇到需要进行组件缓存的场景&#xff0c;这时候Vue提供的keep-alive组件就派上了用场。keep-alive组件是Vue内置的一个抽象组件&#xff0c;它可以将其包裹的组件进行缓存&#xff0c;提高组件的性能&#xff0c;同时也可以节省服务…

普渡机器人CEO预测2024年服务机器人市场将扩大

原创 | 文 BFT机器人 根据普渡科技有限公司的报告&#xff0c;商用服务机器人在东亚地区的应用比其他地方更为广泛。然而&#xff0c;预计到2024年&#xff0c;全球其他地区也将迎头赶上。这家总部位于中国深圳的公司自豪地宣称&#xff0c;它已经成为中国最大的此类机器人出口…

前端excel带样式导出 exceljs 插件的使用

本来用的xlsx和xlsx-style两个插件&#xff0c;过程一步一个坑&#xff0c;到完全能用要消灭好多bug。这时发现了exceljs&#xff0c;真香&#x1f600; 案例 <!DOCTYPE html> <html><head><meta charset"utf-8" /><meta name"view…

详解WebRTC rtc::Thread实现

rtc::Thread介绍 rtc::Thread类不仅仅实现了线程这个执行器&#xff08;比如posix底层调用pthread相关接口创建线程&#xff0c;管理线程等&#xff09;&#xff0c;还包括消息队列&#xff08;message_queue)的实现&#xff0c;rtc::Thread启动后就作为一个永不停止的event l…

如何使用MCSM搭建我的世界Java版服务器并实现远程联机游戏

文章目录 1. 安装JAVA2. MCSManager安装3.局域网访问MCSM4.创建我的世界服务器5.局域网联机测试6.安装cpolar内网穿透7. 配置公网访问地址8.远程联机测试9. 配置固定远程联机端口地址9.1 保留一个固定tcp地址9.2 配置固定公网TCP地址9.3 使用固定公网地址远程联机 本教程主要介…

c语言实现greedy snake(贪吃蛇)

##第一个小项目 大一学生寒假项目 最终实现效果如图 一.以C语言实现个人小项目 在我们快速学完了一个高级编程语言&#xff0c;就应该写一个小项目来加以巩固自己的学习成果。 所以今天&#xff0c;我们来尝试写一写greedy snake&#xff0c;对于大学生来说也是可以加强能…

2,cdc放缩位图

类似地&#xff0c;用pDC->StretchBlt来缩放&#xff0c;只是加上了两个参数&#xff0c;原始位图的宽高。 void CMy1_showbitmapView::StretchBitMap(CDC * pDC) { //CBitmap对象 CBitmap bitmap; //CDC对象 CDC dcMemory; //加载资源 bitmap.LoadBitmapW(IDB_BITMAP1); /…

JUnit

前言&#xff1a;自动化就是selenium脚本来实现的&#xff0c;JUnit是java的单元测试工具&#xff0c;只不过我们在实现自动化的时候需要借助一下JUnit库里面提供的一些方法。 目录 1、Test 2、断言—Assertions类 3、用例的执行顺序 3.1 通过order注解来排序 3.2 参数化 …

delete、truncate和drop区别

一、从执行速度上来说 drop > truncate >> DELETE 二、从原理上讲 1、DELETE DELETE from TABLE_NAME where xxx1.1、DELETE属于数据库DML操作语言&#xff0c;只删除数据不删除表的结构&#xff0c;会走事务&#xff0c;执行时会触发trigger&#xff08; 触发器…

ChatGPT之搭建API代理服务

简介 一行Docker命令部署的 OpenAI/GPT API代理&#xff0c;支持SSE流式返回、腾讯云函数 。 项目地址&#xff1a;https://github.com/easychen/openai-api-proxy 这个项目可以自行搭建 OpenAI API 代理服务器工具&#xff0c;该项目是代理的服务器端&#xff0c;不是客户端。…

IP数据云识别真实IP与虚假流量案例

随着互联网的普及&#xff0c;企业在数字领域面临着越来越复杂的网络威胁。为了保护网站免受虚假流量和恶意攻击的影响&#xff0c;许多企业正在采用IP数据云。本文将结合一个真实案例&#xff0c;深入探讨IP数据云如何成功准确地识别真实用户IP和虚假流量IP&#xff0c;提高网…

【数据分享】1929-2023年全球站点的逐日降雪深度数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 之前我们分享过1929-2023年全球气象站点的逐日平均气温数据、逐日最高气温数据…

计算机网络_1.6.1 常见的三种计算机网络体系结构

1.6.1 常见的三种计算机网络体系结构 1、OSI&#xff08;七层协议&#xff09;标准失败的原因2、TCP/IP参考模型3、三种网络体系结构对比 笔记来源&#xff1a; B站 《深入浅出计算机网络》课程 1、OSI&#xff08;七层协议&#xff09;标准失败的原因 &#xff08;1&#xf…

[SWPUCTF 2021 新生赛]easyupload1.0

发现是上传文件第一想到是文件木马 <?php eval ($_POST[123]);?>木马上传burp修改后缀发现flag里面这个是假的 我们猜想是在phpinfo我们上传<?php eval(phpinfo(););?>木马上传burp修改后缀里面 CtrlF 发现flag

IT行业证书的获取与价值:提升职业竞争力的关键

目录 IT行业证书的价值和作用 1. Cisco&#xff08;思科&#xff09;认证&#xff08;如CCNA、CCNP、CCIE&#xff09;&#xff1a; 2. 微软认证&#xff08;如MCSA、MCSE、MCSD&#xff09;&#xff1a; 3. 计算机网络技术&#xff08;CompTIA Network、CompTIA Security&a…

亲测解决vscode的debug用不了、点了没反应

这个问题在小虎登录vscode同步了设置后出现,原因是launch文件被修改或删除。解决方法是重新添加launch。 坏境配置 win11 + vscode 解决方法 Ctrl + shift + P,搜索debug添加配置: 选择python debugger。 结果生成了一个文件在当前路径: launch内容: {// Use Int…

【jenkins】主从机制及添加Slave节点操作

一、master-slave 日常构建Jenkins任务中&#xff0c;会经常出现下面的情况&#xff1a; 自动化测试需要消耗大量的 CPU 和内存资源&#xff0c;如果服务器上还有其他的服务&#xff0c;可能会造成卡顿或者宕机这样的情况&#xff1b; Jenkins 平台上除了这个项目&#xff0c…

雷达DoA估计的跨行业应用--麦克风阵列声源定位(Matlab仿真)

一、概述 麦克风阵列&#xff1a; 麦克风阵列是由一定数目的声学传感器&#xff08;麦克风&#xff09;按照一定规则排列的多麦克风系统&#xff0c;而基于麦克风阵列的声源定位是指用麦克风拾取声音信号&#xff0c;通过对麦克风阵列的各路输出信号进行分析和处理&#xff0c;…

【计算机网络】物理层概述|通信基础|奈氏准则|香农定理|信道复用技术

目录 一、思维导图 二、 物理层概述 1.物理层概述 2.四大特性&#xff08;巧记"械气功程") 三、通信基础 1.数据通信基础 2.趁热打铁☞习题训练 3.信号の变身&#xff1a;编码与调制 4.极限数据传输率 5.趁热打铁☞习题训练 6.信道复用技术 推荐 前些天发…