当前位置: 首页 > news >正文

C++信号处理

C++ 信号处理


一、信号处理 基础概念

1.1 什么是信号

信号(Signal)是操作系统向进程发送的异步软中断
作用:通知进程发生了异常、外部中断、终止请求等事件。

特点:

  • 异步触发,随时可能打断程序正常执行流
  • 由内核、终端、其他进程、程序自身产生
  • C++ 兼容 C 标准信号机制,头文件:
    #include<csignal>

1.2 信号常见触发来源

  1. 终端按键:Ctrl + CCtrl + \
  2. 程序运行异常:除零错误、非法内存访问、非法指令
  3. 系统内核:进程被杀、超时、管道断开
  4. 代码主动调用:raise()abort()
  5. 其他进程发送信号(Linuxkill命令)

1.3 信号处理核心流程

  1. 定义信号回调处理函数
  2. 使用signal()注册信号与回调
  3. 信号到来时,系统自动中断程序,执行回调
  4. 回调结束后,默认恢复程序执行或退出

二、C++ 标准预定义信号列表

所有信号均定义在<csignal>头文件中:

信号常量含义触发场景
SIGINT2终端中断信号Ctrl + C
SIGABRT6程序异常终止调用abort()
SIGFPE8浮点算术异常除零、数值溢出
SIGILL4非法硬件指令执行无效机器指令
SIGSEGV11段错误非法内存访问、野指针
SIGTERM15软件终止请求系统/其他进程请求结束程序
SIGQUIT3终端退出信号Ctrl + \
SIGALRM14闹钟超时信号定时器到时
SIGBUS7总线错误内存地址对齐错误

注意:部分信号不能被捕获或忽略,如 LinuxSIGKILL


三、signal() 信号注册函数详解

3.1 函数原型

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

拆解说明:

  • 第一个参数sig:要监听的信号编号
  • 第二个参数handler:信号处理函数指针
  • 返回值:返回旧的处理函数指针

3.2 信号处理函数规范

固定格式,必须严格遵守:

void回调函数名(intsignum){// signum:当前触发的信号值// 信号处理逻辑}

3.3 系统内置处理方式

无需自定义函数,直接使用系统宏:

  1. SIG_DFL默认处理
    触发后系统默认行为:终止程序、产生核心转储等
  2. SIG_IGN忽略该信号
    收到信号不做任何处理,程序继续运行

使用示例:

// 忽略 Ctrl+Csignal(SIGINT,SIG_IGN);// 恢复系统默认处理signal(SIGINT,SIG_DFL);

四、raise() 主动发送信号

4.1 函数原型

intraise(intsig);
  • 功能:进程内部主动向自身发送一个指定信号
  • 返回值:成功返回 0,失败返回非0
  • 常用于模拟异常、测试信号回调逻辑

4.2 abort() 主动异常终止

voidabort(void);
  • 内部会触发SIGABRT信号
  • 程序异常终止,可生成 core 崩溃日志

五、完整实战案例

案例1:捕获 Ctrl+C 优雅退出

#include<iostream>#include<csignal>#include<unistd.h>usingnamespacestd;voidsigHandler(intsig){cout<<"\n======================"<<endl;cout<<" 捕获到信号:"<<sig<<" (Ctrl+C)"<<endl;cout<<" 开始释放资源、保存数据..."<<endl;cout<<" 程序安全退出"<<endl;cout<<"======================"<<endl;// 收尾后退出exit(EXIT_SUCCESS);}intmain(){// 注册 SIGINT 信号回调signal(SIGINT,sigHandler);cout<<"程序已启动,按 Ctrl+C 触发退出"<<endl;while(true){cout<<"程序正常运行中..."<<endl;sleep(1);}return0;}

案例2:忽略信号 & 恢复默认处理

#include<iostream>#include<csignal>#include<unistd.h>usingnamespacestd;intmain(){// 忽略 Ctrl+Csignal(SIGINT,SIG_IGN);cout<<"已忽略 Ctrl+C,按 Ctrl+C 无效"<<endl;sleep(5);// 恢复系统默认处理signal(SIGINT,SIG_DFL);cout<<"5秒后恢复默认,现在 Ctrl+C 可终止程序"<<endl;while(true)sleep(1);return0;}

案例3:程序主动 raise 触发信号

#include<iostream>#include<csignal>usingnamespacestd;voidcallback(intsig){cout<<"回调触发,信号编号:"<<sig<<endl;}intmain(){signal(SIGINT,callback);cout<<"程序主动发送 SIGINT 信号"<<endl;// 主动给自己发信号raise(SIGINT);cout<<"信号处理完毕,继续执行后续代码"<<endl;return0;}

案例4:同时注册捕获多个信号

#include<iostream>#include<csignal>#include<unistd.h>usingnamespacestd;voidmultiSigHandler(intsig){switch(sig){caseSIGINT:cout<<"\n收到 Ctrl+C 中断"<<endl;break;caseSIGSEGV:cout<<"\n捕获段错误,非法内存访问"<<endl;exit(1);break;caseSIGFPE:cout<<"\n捕获算术异常(除零/溢出)"<<endl;exit(1);break;default:cout<<"\n收到未知信号:"<<sig<<endl;}}intmain(){// 一次性注册多个信号共用同一个处理函数signal(SIGINT,multiSigHandler);signal(SIGSEGV,multiSigHandler);signal(SIGFPE,multiSigHandler);while(true){cout<<"多信号监听中..."<<endl;sleep(2);}return0;}

六、信号处理函数 严格限制(工程必看)

信号回调函数不能随意写代码,有严格安全限制:

  1. 禁止调用非可重入函数:coutprintfmalloc、文件IO 等(尽量少用)
  2. 禁止复杂业务逻辑、循环、耗时操作
  3. 不要在回调中嵌套信号注册
  4. 建议只做:标记全局状态、简单日志、设置退出标志
  5. 信号是异步的,极易引发竞态条件,多线程环境慎用

推荐最佳实践:

信号回调只修改一个全局标记变量,主循环检测标记再做优雅退出和资源释放。


七、跨平台兼容差异

Linux / macOS

  • 信号机制完整,支持全部标准信号
  • sleep(秒)<unistd.h>
  • 支持kill命令发信号
  • 段错误可生成 core 文件用于崩溃分析

Windows

  • 仅支持子集常用信号:SIGINTSIGABRTSIGFPESIGILLSIGSEGV
  • 睡眠函数:Sleep(毫秒),头文件<windows.h>
  • 不支持 Unix 大量扩展信号
  • 无 core 转储机制

八、工程实际应用场景

  1. 后台服务程序:捕获Ctrl+C优雅退出,关闭socket、保存配置、释放资源
  2. 崩溃防护:捕获段错误、算术异常,记录崩溃现场日志
  3. 守护进程:监听终止信号,做重启或善后处理
  4. 调试定位:捕获异常信号,打印行号、文件信息
  5. 定时任务:利用SIGALRM实现简单定时器

九、核心知识点总结

  1. 信号是操作系统发给进程的异步软中断
  2. signal()注册信号回调,raise()主动发信号;
  3. 可使用SIG_DFL默认处理、SIG_IGN忽略信号;
  4. 常用信号:SIGINTSIGSEGVSIGFPESIGABRTSIGTERM
  5. 信号回调必须精简、可重入,禁止复杂逻辑;
  6. 适合做优雅退出、崩溃捕获、资源善后、服务守护;
  7. Linux 与 Windows 信号子集有差异,跨平台需兼容适配。
http://www.jsqmd.com/news/723732/

相关文章:

  • SeqTrack模型专题全面调研
  • 【附Python源码】基于MLP的波士顿房价预测
  • 性价比高的专用汽车汇总,程力专用汽车可靠吗及行业口碑揭秘 - 工业设备
  • 合规消费增值:从市场痛点到落地玩法,商家、用户、平台都能赢
  • 重载型工业安全地毯,机械作业安全防护优选
  • LDR6500U:Type‑C PD 取电 “一芯通吃”,赋能全场景高效供电
  • 天线测量技术:频谱与网络分析仪应用指南
  • 全志D1s RISC-V开发板:十美元Linux方案解析
  • 记一次 OpenClaw Token 费用优化:从日均 50 美元到 12 美元的技术拆解
  • CBCX平台:多市场接入与跨境交易适配
  • Keithley 2600B系列SMU仪器:高精度电子测试解决方案
  • 用Typer从零搭一个AI命令行工具:我踩过的6个坑
  • 你的数字足迹守护者:为每个应用打造专属位置身份
  • 如何使用 RS® ZNL 设置并执行网络分析仪测量
  • 全网最详细的postman接口测试教程,一篇文章满足你
  • RTX 30/40系显卡实测:用OpenCV CUDA加速图像处理,效率提升多少?
  • 3步解决视频卡顿问题:Flowframes AI插帧实战指南
  • 2026大批量礼盒定制技术干货:从合规到成本的全链路管控 - 优质品牌商家
  • 同态加密密文乘法优化与硬件架构设计
  • vector 核心接口和模拟实现
  • Windows 系统上手动安装 Ubuntu 22.04 到 WSL
  • Python定时任务框架横评:APScheduler vs Celery vs Dramatiq
  • Flutter物流应用的版本控制与依赖管理
  • c++14概述
  • 打造纯净供应链:Ledger官方授权杜绝一切中间风险环节
  • 使用 20 年后告别!Emacs 替代工具开发完成,新工具优势大
  • LLaMA-Factory结合DPO实现偏好对齐(RLHF简化方案)-方案选型对比
  • Fortran数组运算与循环优化实操案例详解
  • 从Django REST framework看NotImplementedError:打造更健壮的API视图与序列化器
  • 模型推理速度翻倍?深入浅出聊聊YOLO里的‘RepConv’重参数化黑科技