学懂C++ (十九):高级教程——深入详解C++信号处理
目录
C++中的信号处理
1. 信号处理的本质
2. 主要信号类型
3. 核心关键点
4. 经典实例
代码分析
5. 进阶:信号屏蔽与多线程
例子:使用sigaction()
6. Windows中的信号处理
7. 比较与总结
示例:Windows控制台事件处理
总结
C++中的信号处理
C++中的信号处理主要涉及操作系统层面的信号机制,尤其是在Unix和Linux系统中。信号是一种异步事件的通知机制,允许程序在特定事件发生时进行响应。常见的用途包括处理异常情况、外部中断等。以下将深入探讨信号处理的本质、信号类型、核心关键点以及经典实例。
1. 信号处理的本质
信号处理的本质在于允许程序响应异步事件。当特定事件发生时,操作系统向进程发送信号,进程通过信号处理程序(信号处理函数)来响应这些信号。信号的用途包括:
- 处理用户请求(如Ctrl+C中断)
- 处理定时器
- 响应硬件异常
- 实现进程间通信
2. 主要信号类型
在Unix/Linux系统中,常见的信号包括:
- SIGINT:中断信号(通常由Ctrl+C触发)
- SIGTERM:终止信号(请求程序终止)
- SIGSEGV:段错误(访问无效内存)
- SIGALRM:定时器到期
3. 核心关键点
在进行信号处理时,需要掌握以下核心要点:
- 信号的注册与处理:可以使用
signal()或sigaction()函数来注册信号处理程序。 - 信号阻塞与解除阻塞:可以用
sigprocmask()控制信号的阻塞,以防在特定代码段内被处理。 - 信号发送:可以使用
kill()函数向进程发送信号。 - 并发性:信号处理是异步的,可能与程序的其他部分并发执行,因此需要注意线程安全。
4. 经典实例
以下是一个简单的信号处理示例,演示如何处理SIGINT信号(通常由Ctrl+C触发)。
#include <iostream>
#include <csignal>
#include <unistd.h> // for sleep()// 信号处理函数
void signalHandler(int signum) {std::cout << "Caught signal " << signum << ", terminating gracefully..." << std::endl;// 这里可以做一些清理工作exit(signum); // 退出程序
}int main() {// 注册信号处理程序signal(SIGINT, signalHandler);std::cout << "Press Ctrl+C to trigger the signal handler..." << std::endl;// 程序主循环while (true) {std::cout << "Running..." << std::endl;sleep(1); // 每秒打印一次}return 0;
}
代码分析
-
注册信号处理程序:使用
signal(SIGINT, signalHandler)注册信号处理函数。当程序接收到SIGINT信号时,调用signalHandler函数。 -
信号处理逻辑:在
signalHandler中进行清理工作并输出通知,最后调用exit(signum)退出程序。 -
主循环:程序在无限循环中持续运行,每秒打印一次“Running...”。按下Ctrl+C时,将捕获到
SIGINT信号,调用处理程序并优雅地终止。
5. 进阶:信号屏蔽与多线程
在多线程程序中,信号处理变得更加复杂。关键要点包括:
- 信号屏蔽:使用
sigprocmask()阻塞某些信号,以防在特定代码块内被处理。 - 专用信号线程:创建专门的线程处理信号,避免信号处理函数与其他线程并发执行的问题。
- 使用
sigaction():sigaction()提供丰富的功能,允许设置信号处理的行为,如恢复默认处理和设置信号屏蔽字。
例子:使用sigaction()
#include <iostream>
#include <csignal>
#include <cstring>
#include <unistd.h>// 信号处理函数
void signalHandler(int signum, siginfo_t *info, void *context) {std::cout << "Caught signal " << signum << ", terminating gracefully..." << std::endl;// 这里可以做一些清理工作exit(signum); // 退出程序
}int main() {struct sigaction action;memset(&action, 0, sizeof(action));action.sa_sigaction = signalHandler; // 设置处理函数action.sa_flags = SA_SIGINFO; // 使用siginfo_t结构// 注册信号处理程序if (sigaction(SIGINT, &action, nullptr) == -1) {std::cerr << "Error registering signal handler" << std::endl;return 1;}std::cout << "Press Ctrl+C to trigger the signal handler..." << std::endl;// 程序主循环while (true) {std::cout << "Running..." << std::endl;sleep(1); // 每秒打印一次}return 0;
}
6. Windows中的信号处理
在Windows系统中,信号的概念与Unix/Linux系统有所不同。Windows使用以下机制处理异步事件:
- 异常处理:使用结构化异常处理(SEH)来处理运行时错误,允许捕获异常并执行处理逻辑。
- 控制台控制处理:通过
SetConsoleCtrlHandler()函数注册控制台控制处理程序,响应如Ctrl+C等事件。
7. 比较与总结
- 信号机制:Unix/Linux提供标准的信号机制,允许程序使用信号进行异步事件处理;而Windows则通过异常处理和控制台事件机制完成类似功能。
- 跨平台性:虽然C++标准库提供了一些跨平台功能,但信号处理在不同操作系统中的实现方式和可用API不尽相同。因此,编写跨平台的信号处理代码时,需要考虑不同操作系统的特性和差异。
示例:Windows控制台事件处理
#include <windows.h>
#include <iostream>BOOL WINAPI ConsoleHandler(DWORD signal) {if (signal == CTRL_C_EVENT) {std::cout << "Caught Ctrl+C! Exiting gracefully..." << std::endl;return TRUE; // 阻止默认处理(程序退出)}return FALSE; // 让系统执行默认处理
}int main() {// 注册控制台控制处理程序SetConsoleCtrlHandler(ConsoleHandler, TRUE);std::cout << "Press Ctrl+C to trigger the handler..." << std::endl;// 程序主循环while (true) {std::cout << "Running..." << std::endl;Sleep(1000); // 每秒打印一次}return 0;
}
总结
C++中的信号处理是一个重要的机制,能够让程序对异步事件做出反应。理解信号的基本概念、如何注册处理程序、信号的阻塞与解除以及多线程环境中的处理逻辑是掌握信号处理的核心。尽管在不同操作系统下信号处理机制存在差异,了解这些差异有助于编写更加健壮和可移植的代码。
相关文章:
学懂C++ (十九):高级教程——深入详解C++信号处理
目录 C中的信号处理 1. 信号处理的本质 2. 主要信号类型 3. 核心关键点 4. 经典实例 代码分析 5. 进阶:信号屏蔽与多线程 例子:使用sigaction() 6. Windows中的信号处理 7. 比较与总结 示例:Windows控制台事件处理 总结 C中的信号…...
SOMEIP_ETS_032:echoUINT8ArrayMinSize
测试目的: 确保DUT能够正确处理最小尺寸的UINT8数组参数,并且在发送和接收过程中保持参数值和顺序不变。 描述 本测试用例旨在验证DUT在处理包含最小尺寸UINT8数组参数的SOME/IP消息时,是否能够准确地发送和接收这些参数,确保返…...
JS+CSS案例:可适应上下布局和左右布局的菜单(含二级菜单)
今天,我给大家分享一个原创的CSS菜单,整个菜单全由CSS写成,仅在切换布局时使用JS。合不合意,先看看效果图。 本例图片 接下来,我来详细给大家分享它的制作方法。 文件夹结构 因为涉及到了样式表切换,所以,你需要借鉴一下我的文件夹结构。 CSS文件夹: reset.css 用于…...
【数据结构】线性表,顺序表
一. 线性表 1. 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 2. 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串... 3. 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理…...
Spring之最基础的创建与Bean的存储与获取(还有Maven加载jar包报错的解决方法)
目录 创建Spring项目 如何解决Maven加载jar包报错 存储Bean和取Bean 在Java中对象也称为Bean。所以后面我提到Bean,大家要知道我说的是什么。 创建Spring项目 我的idea是2022版本的,如果大家和我一样可以参考我的。 1.创建一个Maven项目。图片上忘了…...
RabbitMQ应用问题 - 消息顺序性保证、消息积压问题
文章目录 MQ 消息顺序性保证概述原因分析解决方案基于 spring-cloud-stream 实现分区消费 消息挤压问题概述原因分析解决方案 MQ 消息顺序性保证 概述 a)消息顺序性:消费者消费的消息的顺序 和 生产者发送消息的顺序是一致的. 例如 生产者 发送消息顺序…...
linux tcp通讯demo
linux tcp通讯demo代码。通过用chatgpt生成的代码。做一个代码记录。 一、基本的通讯demo server.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h>…...
在 MongoDB 中,如何配置副本集以实现读写分离?
在 MongoDB 中,配置副本集以实现读写分离主要涉及以下几个步骤: 初始化副本集: 创建副本集时,需要在所有参与节点上运行 rs.initiate() 命令。这将初始化一个新的副本集。 添加成员到副本集: 使用 rs.add() 命令将所有…...
虚拟dom-Diff算法
虚拟dom-Diff算法 vue2 diff算法在vue2中就是patch,通过新旧虚拟dom对比,找到最小变化然后进行dom操作 在页面首次渲染的时候会调用一次patch并创建新的vnode,不会进行深层次的比较,然后再组件中数据发生变化的时候,…...
01创建型设计模式——单例模式
一、单例模式简介 单例模式(Singleton Pattern)是一种创建型设计模式(GoF书中解释创建型设计模式:一种用来处理对象的创建过程的模式),单例模式是其中的一种,它确保一个类只有一个实例ÿ…...
图像分割(一)
一、概述 语义分割:是把每个像素都打上标签(这个像素点是人、树、背景等) 实例分割:不光要区别类别,还要区分类别中的每一个个体 损失函数:逐像素的交叉熵;样本均衡问题 MIOU指标:…...
C++ 新经典:设计模式 目录(先留框架,慢慢来~)
C 新经典:设计模式 C 新经典:设计模式 C 新经典:设计模式第1章 设计模式与软件开发思想、编程环境介绍第2章 模板方法模式第3章 工厂模式、原型模式、建造者模式第4章 策略模式第5章 观察者模式第6章 装饰模式第7章 单件模式第8章 外观模式第…...
go之命令行工具urfave-cli
一、urfave/cli urfave/cli 是一个声明性的、简单、快速且有趣的包,用于用 Go 构建命令行工具。 二、快速使用 2.1 引入依赖 go get github.com/urfave/cli/v2 2.2 demo package mainimport ("fmt""log""os""github.com/ur…...
四种应用层协议——MQTT、CoAP、WebSockets和HTTP——在工业物联网监控系统中的性能比较
目录 摘要(Abstract) 实验设置 实验结果 节选自《A Comparative Analysis of Application Layer Protocols within an Industrial Internet of Things Monitoring System》,作者是 Jurgen Aquilina、Peter Albert Xuereb、Emmanuel Francalanza、Jasmine Mallia …...
MySQL的脏读、不可重复读、幻读与隔离级别
脏读/不可重复读/幻读 脏读 脏读(Dirty Read)发生在一个事务读取了另一个事务尚未提交的数据。如果第二个事务失败并回滚,第一个事务读到的数据就是错误的。这意味着数据从一开始就是不稳定或者“脏”的。 举例 事务A读取了某条记录的值为X。事务B修改该记录的值…...
程序员前端开发者的AI绘画副业之路:在裁员危机中寻找新机遇
正文: 在这个充满变数的时代,作为一名前端开发者,我经历了行业的起伏,见证了裁员危机和中年失业危机的残酷。在这样的背景下,我开始了利用AI绘画作为副业的探索,不仅为了寻求经济上的稳定,更是为…...
Burp Suite的使用和文件上传漏洞靶场试验
第一步:分析如何利用漏洞,通过对代码的查阅发现,代码的逻辑是先上传后删除,意味着,我可以利用webshell.php文件在上传到删除之间的间隙,执行webshell.php的代码,给上级目录创建一个shell.php木马…...
如何在Ubuntu中安装deepin wine版的企业微信
如何在Ubuntu中安装deepin wine版的企业微信 运行如下一条命令将移植仓库添加到系统中 wget -O- https://deepin-wine.i-m.dev/setup.sh | sh自此以后,你可以像对待普通的软件包一样,使用apt-get系列命令进行各种应用安装、更新和卸载清理了。 安装企业…...
案例:Nginx + Tomcat集群(负载均衡 动静分离)
目录 案例 案例环境 案例步骤 部署Tomcat服务器 部署Nginx服务器 实现负载均衡和读写分离 日志控制 案例 案例环境 操作系统 IP 地址 角色 CentOS 192.168.10.101 Nginx服务器(调度器) CentOS 192.168.10.102 Tomcat服务器① CentOS 1…...
【密码学】密码协议的分类:②认证协议
密码协议的分类有很多种方式,这里我采取的是基于协议实现的目的来分类。可以将密码协议分成三类:认证协议、密钥建立协议、认证密钥建立协议。 一、认证协议是什么? 认证协议都在认证些什么东西呢?认证一般要认证三个东西&#x…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
Python的__call__ 方法
在 Python 中,__call__ 是一个特殊的魔术方法(magic method),它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时(例如 obj()),Python 会自动调用该对象的 __call__ 方法…...
