Linux-信号3_sigaction、volatile与SIGCHLD

文章目录

  • 前言
  • 一、sigaction
    • __sighandler_t sa_handler;
    • __sigset_t sa_mask;
  • 二、volatile关键字
  • 三、SIGCHLD
    • 方法一
    • 方法二


前言

本章内容主要对之前的内容做一些补充。


一、sigaction

#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

之前我们学过signal来对信号进行捕捉,sigaction也是一个对信号进行捕捉的系统接口函数,不过sigaction要相对复杂一些。

参数 int signum 是要捕捉的信号编号。

参数struct* sigaction 在这里作为输入型参数,是提供给我们的一个结构体指针类型,这里的结构体名和函数名相同。

参数struct sigaction *oldact 在这里作为输出型参数。

那么struct sigaction 里面有什么呢?

struct sigaction{/* Signal handler.  */
#ifdef __USE_POSIX199309union{/* Used if SA_SIGINFO is not set.  */__sighandler_t sa_handler;/* Used if SA_SIGINFO is set.  */void (*sa_sigaction) (int, siginfo_t *, void *);}__sigaction_handler;
# define sa_handler	__sigaction_handler.sa_handler
# define sa_sigaction	__sigaction_handler.sa_sigaction
#else__sighandler_t sa_handler;
#endif/* Additional set of signals to be blocked.  */__sigset_t sa_mask;/* Special flags.  */int sa_flags;/* Restore handler.  */void (*sa_restorer) (void);};

我们今天主要对函数体内部的sa_handler和sa_mask进行讨论

__sighandler_t sa_handler;

typedef void __signalfn_t(int);
typedef __signalfn_t *__sighandler_t;

根据__sighandler_t的定义,我们可以知道其本质是一个函数指针,所以这里的我们就可以知道其实本质也是像signal一样使用回调函数来进行信号的捕捉。

__sigset_t sa_mask;

typedef __sigset_t sigset_t;

之前我们在学习sigprocmask和sigaddset等信号集接口函数的时候有过接触sigset_t,那么这里的sa_mask是什么呢?

先提出一个观点,在一个信号被处理(递达)过程中,如果同一个信号再次被发送且进入pending表,那么OS是怎样处理的? OS的处理方式是block(阻塞)相同信号,不再重复递达,等到处理完正在被处理的信号再根据情况决定。 而sa_mask在这里的作用就是可以根据其信号集的有效信号,在signum信号正在被处理时,同时阻塞sa_mask的有效信号和其本身信号。

示例代码如下

#include<iostream>
#include<cstdio>
#include<signal.h>
#include<unistd.h>void ShowPending()
{sigset_t pending;sigemptyset(&pending);for (int i = 1; i <= 31; i++){sigpending(&pending);// 通过sigismember来打印我们的pending信号集std::cout << sigismember(&pending, i);}std::cout << std::endl;
}void catchSig(int signum)
{std::cout << "捕捉到" << signum << "信号!" << std::endl; int count = 0;while(1){ShowPending();count++;if(count == 50) break;sleep(5);}
}
int main()
{std::cout << "pid: " << getpid() << std::endl;//1.定义struct sigaction类型struct sigaction act , oldact;//2.mask信号集初始化sigset_t mask;sigemptyset(&mask);//3.mask信号集添加1号,2号,3号, 4号,5号,6号作为有效信号sigaddset(&mask,1);sigaddset(&mask,2);sigaddset(&mask,3);sigaddset(&mask,4);sigaddset(&mask,5);sigaddset(&mask,6);//4.修改act中的数据act.sa_handler = catchSig;act.sa_mask = mask;//5.调用sigactionsigaction(2, &act , &oldact);while(1) sleep(1);return 0;
}

运行结果
在这里插入图片描述

二、volatile关键字

我们之前的学习过程中,也提到过编译器会进行优化,例如我们之前讲的拷贝构造和右值引用都有提到过,而volatile主要解决关于编译器优化所导致的问题。

是的,编译器优化在少数情况下是会造成一些问题的。

而Linux中的gcc编译器是有几种不同程度的优化方案的

-O -O0 -O1 -O2 -O3 -Os -Ofast -Og

在使用gcc或g++命令时,上面的选项从左到右,编译时优化程度依次变大。

示例代码如下

#include<iostream>
#include<cstdio>
#include<signal.h>
#include<unistd.h>int flag = 0;void catchSig(int signum)
{std:: cout << flag ;flag = 1;std::cout << "->" << flag <<std::endl; 
}int main()
{signal(2, catchSig);while(1){if(flag == 1) break;;}std::cout << "程序正常退出" << std::endl;return 0;
}

这段代码如果使用

g++ -o mysignal mysignal.cc -std=c++11

进行编译

结果则是
在这里插入图片描述

这段代码如果使用

g++ -o mysignal mysignal.cc -std=c++11 -O3

进行编译

结果则是
在这里插入图片描述
程序不会自动退出。

这是因为在-O3的优化程度下,编译器检测默认执行流不会修改flag的数据,所以这里的cpu寄存器一直储存着原有的flag值0,导致在判断flag时,一直使用寄存器中的0在判断,导致循环无法退出。

现在我们使用volatile来试试

#include<iostream>
#include<cstdio>
#include<signal.h>
#include<unistd.h>volatile int flag = 0;void catchSig(int signum)
{std:: cout << flag ;flag = 1;std::cout << "->" << flag <<std::endl; 
}int main()
{signal(2, catchSig);while(1){if(flag == 1) break;;}std::cout << "程序正常退出" << std::endl;return 0;
}

在这里插入图片描述
这个时候程序就正常推出了,所以这里volatile的意思就是让告诉编译器不要对flag进行优化,要让寄存器看到内存中的数据!


三、SIGCHLD

SIGCHLD 在子进程停止或者退出时可能收到。

所以我们再学习几种进程等待的方法。

方法一

#include <iostream>
#include <cstdio>
#include <signal.h>
#include <unistd.h>int main()
{signal(SIGCHLD, SIG_IGN);pid_t id = fork();if(id == 0){//子进程sleep(10);exit(0);}//父进程while(1);return 0;
}

将SIGCHLD信号的处理方式变为忽略。
在这里插入图片描述
子进程没有僵尸,而是成功回收。

方法二

#include <iostream>
#include <cstdio>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
void catchCHLD(int signum)
{std::cout << "捕捉到SIGCHLD信号!" << std::endl;int pid = 0;while((pid = waitpid(0,nullptr,WNOHANG)) > 0){std::cout << "成功等待" << pid << "号进程" << std::endl;}
}int main()
{signal(SIGCHLD, catchCHLD);pid_t id = fork();if(id == 0){//子进程sleep(10);exit(0);}//父进程while(1);return 0;
}

在这里插入图片描述
也同样可以成功回收!

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

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

相关文章

Prometheus结合Grafana监控MySQL,这篇不可不读!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

动态规划:LeetCode第10题 正则表达式匹配

题目&#xff1a; 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 . 和 * 的正则表达式匹配。 . 匹配任意单个字符* 匹配零个或多个前面的那一个元素 所谓匹配&#xff0c;是要涵盖 整个 字符串 s的&#xff0c;而不是部分字符串。 示例 1&#xff1a; …

软考57-上午题-【数据库】-数据库的控制功能

一、事务管理 1-1、事务的定义 事务是一个操作序列&#xff0c;这些操作&#xff0c;要么都做&#xff0c;要么都不做。 事务和程序是两个不同的概念&#xff0c;一般一个程序可以包含多个事务。 1-2、事务定义的语句 1、事务开始&#xff1a;BEGIN TRANSACTION 2、事务提…

【ES入门一:基础概念】

集群层面上的基础概念 集群 由多个es实例组成的叫做集群 节点 单个ES的服务实例叫做节点。每个实例都有自己的名字&#xff0c;就是在配置文件中配置的‘node.name’中的内容。为了标识每个节点&#xff0c;每个节点启动后都会分配一个UID&#xff0c;存储在data目录。每个…

线程的同步互斥机制3月4日

题目&#xff1a; 代码&#xff1a; #include <stdio.h> #include <pthread.h> #include <string.h> #include <semaphore.h> #include <unistd.h>sem_t sem1,sem2;void* callback1(void*arg) {while(1){if(sem_wait(&sem1)<0) //等待…

SwiftUI之CoreData详解(一)

coreData 是一种数据持久化的方案&#xff0c;是对SQLite的一种封装。一说到这种桌面化的数据库&#xff0c;我就无比的怀念Foxbase|Foxpro, 多好的数据库产品&#xff0c;被微软扼杀了&#xff0c;相当年教大学生妹子们国家二级数据库时都是手把手教的&#xff0c;呃~~~&#…

Java轻量延迟重试队列实现

背景 很多开放平台都使用Webhook的方式进行外部系统数据同步或者通知&#xff0c;对于Webhook请求的对外发送不进行重试显然有点说不过去。使用简单的while一个条件去重试N次好像达不到什么效果&#xff0c;只能是说有重试而已&#xff0c;而使用消息队列中间件好像依赖又太重…

【论文阅读】High-Resolution Image Synthesis with Latent Diffusion Model

High-Resolution Image Synthesis with Latent Diffusion Model 引用&#xff1a; Rombach R, Blattmann A, Lorenz D, et al. High-resolution image synthesis with latent diffusion models[C]//Proceedings of the IEEE/CVF conference on computer vision and pattern re…

SkyWalking链路追踪上下文TraceContext的追踪身份traceId生成的实现原理剖析

结论先行 SkyWalking 通过字节码增强技术实现&#xff0c;结合依赖注入和控制反转思想&#xff0c;以SkyWalking方式将追踪身份traceId编织到链路追踪上下文TraceContext中。 是不是很有趣&#xff0c;很有意思&#xff01;&#xff01;&#xff01; 实现原理剖析 TraceConte…

1.1_2 性能指标——速率、带宽、吞吐量

文章目录 1.1_2 性能指标——速率、带宽、吞吐量&#xff08;一&#xff09;速率&#xff08;二&#xff09;带宽&#xff08;三&#xff09;吞吐量 1.1_2 性能指标——速率、带宽、吞吐量 &#xff08;一&#xff09;速率 速率即数据率或称数据传输率或比特率。 速率就是“快…

Redis的设计与实现

Redis的设计与实现 数据结构和内部编码 type命令实际返回的就是当前键的数据结构类型&#xff0c;它们分别是&#xff1a;string(字符串)hash(哈希)、list(列表)、set(集合)、zset (有序集合)&#xff0c;但这些只是Redis对外的数据结构。 实际上每种数据结构都有自己底层的…

Docker Protainer可视化平台,忘记登录密码,重置密码。

由于好久没有登录portainer系统&#xff0c;导致忘记了登录密码&#xff0c;试了好多常用的密码都不对&#xff0c;无奈只能重置密码。 一、停止protainer 容器 查看容器ID和COMMAND 用于停止容器 docker ps -a停止容器 docker stop portainer二、查找volume data 宿主机所在…

JavaEE之多线程

一.认识线程 1.多进程实现并发编程的不足之处&#xff1a; 引入多个进程的核心&#xff1a;实现并发编程&#xff08;c的CGI技术就是通过多进程的方式实现的网站后端开发&#xff09;。因为现在是一个多核cpu的时代&#xff0c;并发编程就是刚需。多进程实现并发编程&#xf…

达梦、金仓、南大、瀚高、优炫:从社区建设看企业技术自信心

正文约950字&#xff0c;预计阅读时间2分钟 国产技术厂商在面对自身产品问题时&#xff0c;往往保持回避态度&#xff0c;不愿公之于众&#xff0c;主要原因有2方面&#xff1a; 1&#xff0c;产品技术层面问题较多&#xff0c;如某些根本性缺陷难以攻克&#xff0c;或问题发…

java找工作之Mybatis(入门及xml配置相关)

Mybatis 学习Mybatis就要学会查看官网&#xff0c;官网地址如下&#xff1a;<MyBatis中文网 > 1、简介 1.1什么是Mybatis MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取…

【Vue3】3-6 : 仿ElementPlus框架的el-button按钮组件实

文章目录 前言 本节内容实现需求完整代码如下&#xff1a; 前言 上节,我们学习了 slot插槽&#xff0c;组件内容的分发处理 本节内容 本小节利用前面学习的组件通信知识&#xff0c;来完成一个仿Element Plus框架的el-button按钮组件实现。 仿造的地址&#xff1a;uhttps://…

预充电阻器选型报告

1. 客户基础条件 预充时间 t≤200ms &#xff0c;电容 C1280uf &#xff0c;电池包最高电压 U410V&#xff0c;预充深度 98% &#xff0c;30 秒内连续预充 15 次。 1.1 现选型号 现选EAK预充电阻额定功率 60W&#xff0c;标称阻值为 35Ω&#xff0c;在 此条件下单次预充…

Unity 协程(Coroutine)到底是什么?

参考链接&#xff1a;Unity 协程(Coroutine)原理与用法详解_unity coroutine-CSDN博客 为啥在Unity中一般不考虑多线程 因为在Unity中&#xff0c;只能在主线程中获取物体的组件、方法、对象&#xff0c;如果脱离这些&#xff0c;Unity的很多功能无法实现&#xff0c;那么多线程…

红黑树的简单介绍

红黑树 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出俩倍&#x…

Python类 __init__() 是一个特殊的方法

设计者&#xff1a;ISDF工软未来 版本&#xff1a;v1.0 日期&#xff1a;2024/3/5__init__() 是一个特殊的方法 类似c# C的构造函数 两头都包含两个下划线&#xff0c;这是约定&#xff0c;用于与普通的函数保持区分class User:用户类def __init__(self,first_name,last_name):…