C++学习---信号处理机制、中断、异步环境

news2025/3/11 9:19:36

文章目录

  • 前言
  • 信号处理
    • signal()函数
      • 关于异步环境
    • 信号处理函数示例
    • raise()函数

前言


信号处理

关于信号,信号是一种进程间通信的机制,用于在程序执行过程中通知进程发生了一些事件。在Unix和类Unix系统中,信号是一种异步通知机制,通过发送信号,一个进程可以通知另一个进程发生了某个事件,如按下 Ctrl+C、除零错误等。

在C++中,可以使用 头文件提供的信号处理机制来捕获和处理信号。

信号的基本概念:

  • 信号编号:每个信号都有一个唯一的编号,用来标识不同的事件。例如,SIGINT 是表示中断的信号。
  • 信号处理器: 信号处理器是一个函数,用于处理接收到的信号。你可以为每种信号指定一个处理函数。

常见的信号

  • SIGINT:中断信号,通常由用户按下 Ctrl+C 生成。
  • SIGSEGV:段错误信号,表示非法内存访问。
  • SIGTERM:终止信号,表示进程被要求终止。
  • SIGKILL:强制终止信号,表示进程被强制终止。

signal()函数

C++使用 signal 函数可以为特定的信号注册信号处理函数

语法使用:

void (*signal(int signum, void (*handler)(int)))(int);

也可以写成:

signal(SIGINT, signalHandler);

在上述代码中,

  • signum(SIGINT):要注册的信号的编号。
  • handler(signalHandler):要注册的信号处理函数的指针。

信号处理函数的声明:

void handlerFunction(int signum)

关于信号处理函数的定义应该尽量简单,因为它在异步环境中执行,同时有一些函数(例如‘printf’,‘malloc’)不是异步安全的,所以尽量不要在信号处理函数中使用它们。

关于异步环境

异步环境是指程序执行时存在多个同时运行的线程或进程,这些线程或进程在执行过程中可能会相互干扰,因为它们共享某些资源(如内存、文件描述符等)。在异步环境中,执行顺序是不确定的,因此程序的行为可能受到非常复杂的影响。

printfmalloc 不是异步安全的主要是因为它们在执行时可能涉及到对共享资源的访问,而这样的访问在异步环境中是不安全的。

  1. printf:
    • print函数通常会使用标准输出(stdout),而在异步环境中,多个线程或进程可能会同时尝试写入标准输出,导致输出内容混乱。
    • 在标准库中的输出函数(如 printf)通常使用全局锁(mutex)来保护对输出流的访问,但这并不能解决所有的异步安全问题。在信号处理函数中使用 printf 可能导致死锁或其他竞态条件。
  2. malloc:
    • malloc 函数用于动态分配内存,而在异步环境中,多个线程或进程可能同时尝试分配或释放内存,这可能导致内存管理错误。

因此,在异步环境中,为了确保代码的正确性,应该尽量避免在信号处理函数或多线程环境中使用不可重入(non-reentrant)的函数。不可重入函数是指在执行过程中依赖于全局状态静态变量的函数,而这在异步环境中可能导致不确定的结果。

为了在异步环境中安全使用输出函数和内存分配函数,通常建议使用异步安全的替代版本。例如,在信号处理函数中,可以使用 write 函数代替 printf,而在多线程环境中,可以使用 pthread 库提供的线程安全的输出函数和内存分配函数。


信号处理函数示例

#include <iostream>
#include <csignal>

// 信号处理函数
void signalHandler(int signum) {
    std::cout << "Received signal: " << signum << std::endl;

    // 自定义处理逻辑可以在这里添加
    // ...

    // 恢复对 SIGINT 的默认处理
    signal(SIGINT, SIG_DFL);
}

int main() {
    // 注册信号处理函数
    signal(SIGINT, signalHandler);

    std::cout << "Press Ctrl+C to trigger the signal." << std::endl;

    // 一个简单的循环,使程序保持运行
    while (true) {
        // 等待信号的到来
    }

    return 0;
}

在上述代码中,声明了一个自定义的信号处理函数signalHandler;之后的main函数中,用signal注册信号处理函数,来处理SIGINT信号。signal函数的第一个参数就是要识别的信号的编号,第二个参数就是指向信号处理函数的指针。

而在信号处理函数signalHandler中,可以添加自定义的处理逻辑。

上述代码运行后,因为while(ture),程序会一直保持运行,直到我们按下Ctrl+C发生中断后,signal函数在捕获信号后,信号处理函数发挥作用,打印了Received signal: 2;

因为SIGINT 的信号编号是 2,所以signum的值是2;

如果想要在信号处理完成后恢复对该信号的默认处理,可以使用 signal(SIGINT, SIG_DFL)。

忽略和恢复信号:

  • 使用 signal(SIGINT, SIG_IGN) 可以忽略 SIGINT 信号。
  • 使用 signal(SIGINT, SIG_DFL) 可以恢复对 SIGINT 的默认处理。

关于恢复信号,当你按下 Ctrl+C 触发 SIGINT 信号时,如果没有 signal(SIGINT, SIG_DFL); 这一行,那么程序将继续执行 signalHandler 函数,但不会将 SIGINT 的处理方式恢复为默认。这意味着如果再次按下 Ctrl+C,signalHandler 函数将再次被调用,而不会终止程序。

实际上,如果不将 SIGINT 恢复为默认处理方式,程序可能会对多次 Ctrl+C 信号作出相应,而不是默认的行为(终止程序)。


raise()函数

raise 函数是用于在程序中手动触发一个信号的函数。

声明:

int raise(int sig);
  • sig:要触发的信号的编号。

raise 函数返回一个整数值,表示函数调用的结果。如果成功发送信号,返回 0;如果失败,返回非零值。

示例:

#include <csignal>
#include <iostream>


// 信号处理函数
void signalHandler(int signum) {
    std::cout << "Received signal: " << signum << std::endl;
}

int main() {
    // 注册信号处理函数
    signal(SIGINT, signalHandler);

    std::cout << "Press Ctrl+C to trigger the signal." << std::endl;

    // 模拟其他程序逻辑
    int count = 0;
    while (true) {
        // 模拟其他程序逻辑
        std::cout << "Working... (" << count << ")" << std::endl;

        // 在某个条件下手动触发 SIGINT 信号
        if (count > 500) {
            std::cout << "Manually triggering SIGINT..." << std::endl;
            raise(SIGINT);
        }

        // 模拟其他程序逻辑
        // ...
        

        // 增加计数
        count++;
    }

    return 0;
}

上述代码的运行结果:

在这里插入图片描述

可以看到在进行俩次的模拟生成信号后,程序就停止了,这是因为没有重新注册 SIGINT 的处理函数,程序将使用默认的信号处理方式,即终止程序。

如果想要第一次信号处理后继续运行,可以重新注册 SIGINT 的处理函数。
在signalHandler函数中添加:

// 重新注册信号处理函数
	signal(SIGINT, signalHandler);

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

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

相关文章

MySQL Command Line Client 运行闪退问题解决,缺少my.ini文件

MySQL Command Line Client 运行闪退问题解决&#xff1a; 问题排查&#xff1a; 1.找到Command Line Client的路径位置&#xff0c;并查看属性&#xff0c;步骤截图&#xff1a; 查看属性&#xff1a; 查看属性中的目标路径&#xff1a; 2.进入属性中的目标路径&#xff0c;…

CSS布局001:画各种三角形

CSS实战中&#xff0c;有很多时候采用css来绘制三角形&#xff0c;而不是采用图片的方式&#xff0c;这样有利于快速成型&#xff0c;不用多调用服务器数据。 CSS代码 上三角 #triangle-up {width: 0;height: 0;border-left: 50px solid transparent;border-right: 50px solid…

Spring -Spring之循环依赖源码解析

什么是循环依赖&#xff1f; 很简单&#xff0c;就是A对象依赖了B对象&#xff0c;B对象依赖了A对象。 比如&#xff1a; // A依赖了B class A{public B b; }// B依赖了A class B{public A a; }那么循环依赖是个问题吗&#xff1f; 如果不考虑Spring&#xff0c;循环依赖并…

2023.11.11 关于 Spring 中 Bean 的作用域

目录 Bean 的作用域 作用域的定义 Singleton&#xff08;单例作用域&#xff09; Prototype&#xff08;原型作用域&#xff09; Request&#xff08;请求作用域&#xff09; Session&#xff08;会话请求&#xff09; Application&#xff08;全局作用域&#xff09; …

计算机毕业设计 基于Springboot的影院购票管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

立冬特辑-----链表OJ题优选合集~~

目录 ​​​​​​​前言&#x1f333; 1.链表中倒数第k个结点&#x1f338; 1.1 思路 1.2 代码 2. 链表的回文结构&#x1fab8; 2.1 思路 2.2 代码 3.相交链表&#x1f32a;️ 3.1 思路 3.2 代码 4.环形链表I&#x1f30a;&#x1f6f3;️ 4.1 思路 4.2 代码 4…

大数据治理——为业务提供持续的、可度量的价值(二)

第二部分&#xff1a;元数据集成体系结构 在明确了元数据管理策略后需要确定实现该管理策略所需的技术体系结构&#xff0c;即元数据集成体系结构。元数据集成体系结构涉及到多个概念&#xff0c;如元模型、元-元模型、公共仓库元模型&#xff08;CWM&#xff09;等&#xff0…

人工智能时代,掌握未来技术趋势,成为领先者

人工智能时代&#xff0c;掌握未来技术趋势&#xff0c;成为领先者 摘要&#xff1a;本文将从热门技术趋势、技术应用场景、技术实践案例、技术发展趋势等角度进行探讨&#xff0c;帮助读者更好地了解技术前沿动态&#xff0c;为自身职业发展提供指导。在人工智能时代&#xf…

作业提交产品的爽点、痛点和痒点分析以及开源软件介绍

目录 1 爽点1.1 什么是爽点1.2 如何构成爽点1.3 案例 2 痛点2.1 什么是痛点2.2 如何构成痛点2.3 案例分析 3 痒点3.1 什么是痒点3.2 如何构成痒点3.3 案例分析 4 开源软件Canvas LMS4.1 链接地址4.2 简介4.3 特点和功能4.4 功能展示图片 1 爽点 1.1 什么是爽点 即时满足&#…

阿里云-maven私服idea访问私服与组件上传

1.进入aliyun制品仓库 2. 点击 生产库-release进入 根据以上步骤修改本地 m2/setting.xml文件 3.pom.xml文件 点击设置获取url 4. idea发布组件

C语言--每日五道选择题--Day8

第一题 1、下列程序的输出是&#xff08; &#xff09; #include<stdio.h> int main() {int a[12] {1,2,3,4,5,6,7,8,9,10,11,12};int *p[4];int i;for(i0;i<4;i){p[i]&a[i*3];}printf("%d\n"&#xff0c;p[3][2]);return 0; } A: 上述程序有错误 B: 6…

在Win11中使用docker安装Oracle19c

在Win11中使用docker安装Oracle19c 首先是去docker官网下 docker for windows安装oracle19c首先下载image运行镜像在工具中登录可能遇到的问题 首先是去docker官网下 docker for windows 官网&#xff1a; https://www.docker.com/get-started/ 如果Windows是专业版&#xff0…

[100天算法】-面试题 04.01.节点间通路(day 72)

题目描述 节点间通路。给定有向图&#xff0c;设计一个算法&#xff0c;找出两个节点之间是否存在一条路径。示例1:输入&#xff1a;n 3, graph [[0, 1], [0, 2], [1, 2], [1, 2]], start 0, target 2 输出&#xff1a;true 示例2:输入&#xff1a;n 5, graph [[0, 1], …

微信的聊天记录导出到网页中的最快方法,语音能听,图片视频能看

12-7 如果你有把微信的聊天记录导出到表格或者网页上的需求&#xff0c;适合看看本文章&#xff0c;本文的方法可以让你把微信的聊天记录导出备份&#xff0c;可以在完全脱离微信的情况下随时调取查看聊天数据。 本文介绍的软件可以导出两种格式的聊天记录备份文件&#xff0…

C/C++数字判断 2021年9月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C数字判断 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C数字判断 2021年9月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 输入一个字符&#xff0c;如何输入的字符是数字&#x…

在线生成二维码--支持彩色二维码和包含Logo

具体请前往&#xff1a;在线二维码生成工具--可将网址等内容生成为指定大小&#xff0c;指定颜色的彩色二维码,同时支持添加Logo

win环境Jenkins高级配置各种插件和启动jar包

今天分享Jenkins高级配置各种插件&#xff0c;在看此篇之前必须先了解上一篇博客内容&#xff0c;因为此篇是在上篇的基础上完善的&#xff1a; 一、git仓库的多分支选择 想要多分支选择部署&#xff0c;需要全局安装Git parameter 插件 1、点击入口 来到 2、点击进入 安装一…

第24章_mysql性能分析工具的使用

文章目录 1. 数据库服务器的优化步骤2.查看系统性能参数3. 统计SQL的查询成本&#xff1a;last_query_cost4. 定位执行慢的 SQL&#xff1a;慢查询日志4.1 开启慢查询日志参数4.2 查看慢查询数目4.3 测试慢sql语句&#xff0c;查看慢日志4.4 系统变量 log_output&#xff0c; l…

Day03:注意事项、this关键字、构造器、JavaBean、String、ArrayList

OOP的注意事项 类名要跟class文件名一致&#xff08;一个class可以有多个类&#xff0c;但只有一个public&#xff0c;且与文件名一致&#xff09;&#xff0c;命名介意大驼峰&#xff1b;如果某个对象没有变量指向他&#xff0c;就成垃圾对象了&#xff08;空指针&#xff09…

【网络奇遇记】你真的了解网络中的电路交换、分组交换和报文交换吗?

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. 电路交换1.1 电路交换讲解1.2 电路交换实例 二. 分组交换1.1 分组交换讲解1.2 分组交换实例1.3 …