Linux——进程信号(下)

news2024/11/19 19:31:42

目录

总结

一,信号保存

1.1 阻塞信号

2.2 信号在内核(操作系统)中的表示

2.3 系统接口

2.3.1 sigset_t信号集

2.3.2 信号集的操作函数

2.3.3 sigprocmask

 2.3.4 sigpending

2.4 实验样例

三,信号处理

3.1 信号捕捉

 3.2 sigaction接口

3.3 实验样例

 总结


总结

上一篇我们主要讲了进程信号的信号发出,本文主要讲解后两个部分,信号保存和信号处理

一,信号保存

1.1 阻塞信号

在讲解信号是如何保存之前,我们需要先认识一下什么是阻塞信号

首先,在我们进程信号中,实行执行信号的处理动作称为递达 ;而信号从产生到递达的过程叫做未决。而在上面两个过程中,我们可以选择阻塞某个信号,被阻塞的信号会一直处于未决的状态无法递达,直到信号阻塞被解决,信号才会被执行。注意,这里的阻塞和忽略是两码事,阻塞行为是在未决和递达直接,而忽略是递达的一种处理方式。

2.2 信号在内核(操作系统)中的表示

上一遍我们简单讲了一下信号是在内核创建的task_struct中以位图的数据结构表示的,本文就详细讲解一下信号在task_struct中的储存结构

 

 信号在task_struct中的存储结构如上图所示,在结构体中存储的有三个变量block,pending,handler用来表示信号,接下来我们就详细解释一下这三个的变量的含义。

  1. block:32位整形,位图数据结构,其中每个比特位的含义对应的是上面2图中前31个常见的信号,每比特位的内容的含义是有没有阻塞对应信号,1表示阻塞,0表示未阻塞
  2. pending:32位整形,位图数据结构,其中每个比特位的含义对应的是上面2图中前31个常见的信号,每比特位的内容的含义是有没有接收到对应信号,1表示存在,0表示不存在
  3. handler:一个函数指针数组,其中每个下标表示的是图二对应的前31个信号,每个下标的内容则是对应信号的处理方式,SIG_DFL为默认处理方式,SIG_IGN为忽略,函数指针则是自定义处理方式。

当一个信号发送过来时,在储存结构中的流程如下,先在pending中将信号对应比特位置为1,然后在block中检查信号是否被阻塞,如果被阻塞则等待阻塞状态消失再进行处理,否则在合适的时间进行处理,处理时在根据handler中信号对应处理方式进行处理

 上面的task_struct中的信号储存结构,我们需要注意该结构是由内核直接进行管理,而我们用户无法直接插手管理,只能通过内核提供的系统接口控制信号的储存,接下来我们就讲一下内核提供的控制信号储存的系统接口

2.3 系统接口

我们将系统接口主要是先将每个接口的用法含义讲一下,最后在写一个程序用一下这些接口

2.3.1 sigset_t信号集

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

2.3.2 信号集的操作函数

在Linux下我们可以通过下面的指令查找信号集的操作函数

man sigemptyset

  1. sigemptyset:初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号。
  2. sigfillset:初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。
  3. sigaddset和sigdelset:指的是增加或者删除set中的某种有效信号
  4. sigismember:检测set中是否由某种信号

注意:在使用sigset_t前必须要用sigemptyset或者sigfillset初始化,使信号集处于确定的初始状态。

这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种 信号,若包含则返回1,不包含则返回0,出错返回-1

2.3.3 sigprocmask

现在我们讲一下如何读取改进进程的信号屏蔽字(阻塞信号集)

 如图想要修改进程的阻塞信号集,需要用系统接口sigprocmask,其中how为修改方式,如果set为非空信号集,则将阻塞信号集set为基地修改阻塞信号集,然后为了保证安全性,会将修改前的阻塞信号集备份放到oldset中传回。how的修改方式有如下几种:

 如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。

 2.3.4 sigpending

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

2.4 实验样例

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
using namespace std;

void pendingprint(sigset_t* pendingset)
{
    int i=1;
    for(i=1;i<=31;i++)
    {
        if(sigismember(pendingset,i)==1)
        {
            cout<<1;
        }
        else
        {
            cout<<0;
        }
    }
}

int main()
{
    sigset_t set,oset;
    //1.1 初始化
    sigemptyset(&set);
    sigemptyset(&oset);

    //1.2 向屏蔽信号集群加入SIGINT(ctrl+c)
    sigaddset(&set,SIGINT);

    //1.3 改进进程的屏蔽信号集
    sigprocmask(SIG_BLOCK,&set,&oset);

    while(true)
    {
        //2.1获取进程的pending信号集
        sigset_t pendingset;
        sigpending(&pendingset);

        //2.2打印pendingset信号集
        pendingprint(&pendingset);
        cout<<endl;

        sleep(1);

    }
    return 0;
}

 代码如上所示,主要分两个板块第一个就是对信号集完成初始化并且添加SIGINT信号,然后修改进程的屏蔽信号集;第二步是打印观察进程的pending信号集,主要观察当我们发送SIGINT前后的变化

 结果如上,我们发现当我们发送SIGINT信号号,其确实由于阻塞信号集而一直处于未决的阶段

三,信号处理

首先,信号处理并不是立即处理,而是寻找合适的时机进行处理。为什么呢?因为信号的产生是异步的,当信号发送过来时进程可能在处理更重要的事情,会等待合适的时机处理信号。那么合适的时机又是什么呢?指的是当进程从内核态切换到用户态时,进程会在内核的指导下,进行信号的检测以及处理。

 那么什么是内核态和用户态呢?

首先,用户态指运行用户自己的代码时的状态;内核态指的是发生时钟中断或者调用系统接口是会由用户态转变为内核态。由于内核态讲起来比较复杂,因此我们这里大致讲一下,在正常的32位4G的进程地址空间中,前3G属于用户区,存储的是堆栈代码数据等,3G-4G的空间存储的是操作系统的代码和数据,而CPU中会有一个专门的寄存器记录进程的状态,0是内核态,3是用户态。

用户态转为内核态主要有两种方式:

  1. 每隔一段时间硬件会向内核发送时钟中断,此时会由用户态转为内核态,内核态切换进程也是在这个时候
  2. 系统调用,当调用系统接口时,会转化为内核态调用接口

3.1 信号捕捉

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。而信号的捕捉具体方式如下图

在上图中我们以信号SIGHA为自定义处理方式为例子,在执行某条代码时,可能会因为时钟中断,异常,系统调用等行为进入内核处理,当内核处理完准备回用户模式之前,会检测当前过程中可以递送的信号,如图SIGHA的处理方式为自定义,当要处理SIGHA时会回到用户模式调用SIGHA的自定义处理函数,处理后会调用sigreturm再次回到内核,然后在返回用户模式之前会再次检测当前进程中是否有可以递送的信号,并重复上面的行为

 3.2 sigaction接口

 sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。sigaction结构体具体如下:

 

 在该结构体中,sa_handler为制定编号所自定义的处理方式,sa_mask则是在处理过程中需要屏蔽的额外的信号,为什么有sa_mask呢?我们需要了解到,当我们在处理一个信号时,进程会将该信号自动加入进程的阻塞集中,这样就保证了在处理这个信号时,即使又接收到了这个信号,它也会被阻塞到该信号处理结束为止。

3.3 实验样例

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
using namespace std;

void pendingprint(sigset_t* pendingset)
{
    int i=1;
    for(i=1;i<=31;i++)
    {
        if(sigismember(pendingset,i)==1)
        {
            cout<<1;
        }
        else
        {
            cout<<0;
        }
    }
}

void handler(int signal)
{
    int i=0;
    //2.2 定时15秒后结束处理观察发生什么
    while(i<15)
    {
        //2.3 打印进程的pending信号集
        sigset_t pendingset;
        sigpending(&pendingset);
        pendingprint(&pendingset);
        cout<<endl;
        
        i++;
        sleep(1);
    }
}
int main()
{
    
    //1.1 创建结构体act oldact
    struct sigaction act;
    struct sigaction oldact;

    //1.2 将act中的处理方式改为自定义处理方式
    act.sa_handler=handler;
    act.sa_flags=0;

    //1.3添加额外的需要阻塞的信号3,4,5
    sigaddset(&act.sa_mask,3);
    sigaddset(&act.sa_mask,4);
    sigaddset(&act.sa_mask,5);

    //2.1 修改2信息号的处理方式为自定义处理
    sigaction(2,&act,&oldact);

    while(true)
    {
        cout<<getpid()<<endl;
        sleep(1);
    }

    
    return 0;
}

代码如上图,我们主要修改2信号的处理方式为handler,handler主要是在15秒内一直打印pending信号集,我们可以借此观察在处理信号时对sa_mask中信号的阻塞效果,以及处理结束后的情况,结果如下

 

 如图,当我们运行后发送信号2,我们看到进程循环打印pending信号集,此时当我们发送信号3,4我们发现由于我们把其加入了阻塞信息集,导致2,3,4信号都被阻塞,当15秒后handler结束,我们发现信号阻塞消失,信号由操作系统处理

 总结

linux的进程信号到这里就结束了,希望铁子们能够有所收货。

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

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

相关文章

2022年真题 - 17 - 系统优化

系统优化 题目配置验证配置 题目 StorageSrv - 系统优化 系统资源限制设置&#xff1a;设置所有用户的硬件跟软件的最大进程数、最大文件打开数为65535&#xff1b;开启 IPv4 恶意 icmp 错误消息保护&#xff1b;开启 SYN 洪水攻击保护&#xff1b;允许系统打开的端口范围为 …

服务网关 Gateway

服务网关 Gateway 服务网关介绍Gateway 介绍Gateway 和 Nginx 网关的区别Gateway 核心概念Gateway工作流程 Gateway 案例Predicate&#xff08;断言&#xff09;After 路由谓词工厂Before路由谓词工厂Between 路由谓词工厂Cookie路由谓词工厂Header 路由谓词工厂Host 路由谓词工…

支付宝支付(六):小程序支付(Go+Gin+内网穿透)

一、前置条件 &#xff08;1&#xff09;go语言&#xff0c;1.18 &#xff08;2&#xff09;Gin、第三方依赖包&#xff1a;gopay【github.com/go-pay/gopay/alipay】https://github.com/go-pay/gopay/blob/main/doc/wechat_v3.md &#xff08;3&#xff09;支付宝支付相关信…

Java面向对象程序开发——JDK8新特性

文章目录 网络编程入门知识JDK8新特性Lambda表达式以多线程为例&#xff1a;Lambda结合for循环&#xff1a; Stream流获取一个流的2种常用的方式&#xff1a;常用方法终结方法延迟方法 案例 网络编程入门知识 软件结构、协议分类、网络通信协议、网络编程三要素、TCP通信协议、…

爬虫的分布式思维与实现思路

爬虫的分布式思维与实现思路 基本构架 scrapy-redis实现分布式&#xff0c;其实从原理上来说很简单&#xff0c;这里为描述方便&#xff0c;我们把自己的核心服务器称为master&#xff0c;而把用于跑爬虫程序的机器称为slave 我们知道&#xff0c;采用scrapy框架抓取网页&…

netty学习(4):通过SpringBoot Web发送消息实现netty实现多个客户端与服务器通信

1. 基于netty学习&#xff08;3&#xff09;:SpringBoot整合netty实现多个客户端与服务器通信的学习&#xff0c;要想通过http在netty客户端之间发送消息&#xff0c;需要实现spring-boot-starter-web&#xff0c;封装消息格式&#xff0c;自动调用netty客户端 2. 封装一个简单…

算法笔记——哈希表篇

一般哈希表都是用来快速判断一个元素是否出现集合里&#xff0c;哈希表并不意味着一定要使用HashMap&#xff0c;有时候使用数组更方便&#xff0c;有时候要使用set&#xff0c;依据具体情况而定&#xff0c;哈希表是典型的空间换时间。 数组作为哈希表 一些应用场景就是为数组…

工具 | 应用程序无法启动,应为应用程序的并行配置不正确

工具 | 应用程序无法启动&#xff0c;应为应用程序的并行配置不正确 “E:\02-Doc\朱老师物联网大讲堂-全部视频\朱有鹏老师嵌入式linux核心课程\开发版光盘资料\X210V3S_A\tools\x210_Fusing_Tool.exe”的激活上下文生成失败。 找不到从属程序集 Microsoft.VC90.MFC,processorA…

DNS-去中心化域名系统,创建您在DeSoc 社会中的YUAN ID

传统域名系统 (DNS) 是一个分层的分散信息存储&#xff0c;用于将用户在网络浏览器中输入可读名称&#xff08;例如 www.baidu.com&#xff09;解析为IP地址&#xff0c;来访问互联网上的计算机。传统DNS使用一种分布式数据库&#xff0c;有严格的上下级关系&#xff0c;上级仅…

[Android JNI] --- JNIEnv和JavaVM

1 JVMEnv 1.1 JNIEnv 是什么 JNIEnv 即 Java Native Interface Environment&#xff0c;Java 本地编程接口环境。JNIEnv 内部定义了很多函数用于简化我们的 JNI 编程。 JNIEnv是提供JNI Native函数的基础环境&#xff0c;线程相关&#xff0c;不同线程的JNIEnv相互独立&#…

SpringBoot快速回顾(@value读取配置文件)

目录 1.定义配置文件2. 定义Controller类3. 测试4. 优化4.1 封装实体类4.3 定义controller类4.2 测试 本文将介绍如何使用value读取配置文件的内容。 在实际项目中&#xff0c;往往会在配置文件中写项目部署需要配置的环境信息&#xff08;数据库驱动&#xff0c;数据库账号密码…

医疗金融法律大模型:从ChatDoctor到FinBERT/FinGPT/BloombergGPT、ChatLaw/LawGPT_zh

第一部分 各种医疗类ChatGPT&#xff1a;或中英文数据微调LLaMA、或中文数据微调ChatGLM 1.1 基于LLaMA微调的中英文版ChatDoctor 1.1.1 ChatDoctor&#xff1a;通过self-instruct技术提示API的数据和医患对话数据集微调LLaMA Github上有一个基于LLaMA模型的医疗微调模型&am…

zabbix (自定义监控内容-配置邮件报警-自动发现与自动注册)

目录 zabbix 客户端主机配置自定义监控内容设置邮件报警zabbix 自动发现与自动注册zabbix 自动发现&#xff08;对于 agent2 是被动模式&#xff09;//zabbix 自动发现&#xff08;对于 agent2 是被动模式&#xff09;zabbix 自动注册&#xff08;对于 agent2 是主动模式&#…

IDEA+springboot+jpa+Layui+Mysql销售考评系统源码

IDEAspringbootjpaLayuiMysql销售考评系统源码 一、系统介绍1.环境配置 二、系统展示1. 管理员登录2.评分结果3.评分管理4.添加评分5.用户管理6.添加用户7.角色管理8.添加角色8.销售管理9.添加销售 三、部分代码UserDao.javaUserController.javaUser.java 四、其他获取源码 一、…

计算机组成原理实验二:多位逻辑门构建

目录 一、实验目的 二、实验设备 三、实验原理 四、实验内容 1. 16位非门 2.16位与门 3.16位或门 4. 16位复用器 五、实验习题 1.还可以怎样设计各种芯片的物理结构 2.“block copy”&#xff08;块复制&#xff09;和edit菜单中“copy to clipboard”的区别 六、自…

在线OJ项目

1.在线OJ-背景介绍 在线的网页版的编程平台.&#xff0c;打开一个网站,上面就能看到很多的算法题.&#xff0c;在线做题,在线提交.立即就能看到运行结果,是否通过. leetcode 牛客等 一个在线OJ平台,核心功能: 能够管理题目(保存很多的题目信息:题干&#xff0b;测试用例)题…

FPGA软核调试方法

软核工程创建步骤 创建如下工程目录 bin目录&#xff1a;存放SDK工程生成的elf文件(Release编译模式) hdf目录&#xff1a;存放fpga工程师提供的的hdf文件 prj目录&#xff1a;工程目录(包含SDK工程源码) doc目录&#xff1a;文档目录 基于2018.2版本SDK建立工程 打开Xil…

Spring Boot中的CSRF攻击及预防

Spring Boot中的CSRF攻击及预防 什么是CSRF攻击&#xff1f; CSRF&#xff08;Cross-site Request Forgery&#xff09;跨站请求伪造&#xff0c;也称为“one-click attack”或“session riding”&#xff0c;是一种网络攻击方式&#xff0c;攻击者通过在受害者浏览器上欺骗或…

【redis】生产级部署

目录 环境部署 redis环境部署 redis多实例配置 构建redis cluster集群 cluster生产集群部署 Cluster集群故障切换 环境部署 1 、关闭防火墙 2 、准备两台虚拟机配置内容如下 redis-master 192.168.108.67 7000 redis-master01 7001 redis-master02 7002 redis-ma…

Vue2.0-3.0 入门到实战 - 初始及插件安装

1 创建view实例,初始化渲染 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body><div id"app">{{ msg }} </div><script type&…