惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - Franky
有赞技术团队
有赞技术团队
博客园 - 司徒正美
量子位
N
News and Events Feed by Topic
T
Threatpost
Last Week in AI
Last Week in AI
D
Darknet – Hacking Tools, Hacker News & Cyber Security
酷 壳 – CoolShell
酷 壳 – CoolShell
C
CERT Recently Published Vulnerability Notes
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
I
Intezer
人人都是产品经理
人人都是产品经理
T
Tenable Blog
IT之家
IT之家
雷峰网
雷峰网
腾讯CDC
博客园 - 聂微东
V
Visual Studio Blog
S
SegmentFault 最新的问题
Scott Helme
Scott Helme
Spread Privacy
Spread Privacy
月光博客
月光博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
大猫的无限游戏
大猫的无限游戏
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
T
Tailwind CSS Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
N
Netflix TechBlog - Medium
J
Java Code Geeks
宝玉的分享
宝玉的分享
F
Full Disclosure
WordPress大学
WordPress大学
A
Arctic Wolf
小众软件
小众软件
AWS News Blog
AWS News Blog
Attack and Defense Labs
Attack and Defense Labs
NISL@THU
NISL@THU
AI
AI
Hugging Face - Blog
Hugging Face - Blog
F
Fortinet All Blogs
云风的 BLOG
云风的 BLOG
N
News | PayPal Newsroom
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org

博客园 - 海乐学习

Java Maven 开发的常用命令 C语言开发的常用命令 三汇Linux配置Config说明 Java实现优雅的关闭程序并执行清理流程的写法 将C语言开发的程序做成 麒麟系统的服务(Systemd 标准服务,Linux 通用) 实现开机自启动 ZeroMQ的DEALER双帧结构(路由空帧 + 业务帧)(支持异步收发、主动推送事件)C写服务端 java写客户端 ZeroMQ中ZMQ_DEALER单帧数据(支持异步收发、主动推送事件)C写服务端 java写客户端 win10系统中 关闭专注助手 执行 xx.sh 脚本文件时出现: /bin/bash^M:解释器错误: 没有那个文件或目录 运行编译打包好的 xxx.jar 中没有主清单属性 出现这个错误 将Java编译的 .jar文件做成 麒麟系统的服务(Systemd 标准服务,Linux 通用) 实现开机自启动 将Java编译的 .jar文件做成windows服务 实现开机自启动 方法二 RabbitMQ在麒麟系统中离线安装说明 VMware启动虚似机后出现 无法获取快照信息: 锁定文件失败 模块“Snapshot”启动失败。未能启动虚拟机。 C语言在Linux中开发完整Demo包含读配置文件写日志和定时器Timer C语言在Linux中开发读取配置文件app.conf 麒麟V10 Server系统中搭建C语言开发环境 麒麟ServerV10 修改IP4地址 麒麟ServerV10 配置IP4 当系统中有两个版本的Maven时,用IDEA创建Maven有时会出错 麒麟ServerV10安装 espeak-ng 和 ffmpeg 方法 C语言在Linux中开发没有界面纯后台运行的Demo程序(含日志和Timer) C语言在Linux中开发使用定时器Timer在界面上显示时间 C语言在Linux中开发带界面的程序(含每小时日志) C语言在Linux中开发第一个项目Hello Word 在apache-maven项目中使用log4写日志 在apache-maven项目中解决中文乱码问题 在apache-maven项目中读取配置文件 创建apache-maven项目 远程桌面连接时出现身份验证错误 要求的函数不受支持 这可能是由于CredSSP加密数据库修正 win10系统查看电脑从锁屏状态回到使用状态 apache-maven的常用命令 C语言在 Linux 中的常用命令 apache-maven安装配置 麒麟CentOS下安装ZeroMQ开发包 Window上用VS Code + Remote-SSH组件的方式来实现开发编译Linux上的C++程序 win10弹出 无法使用内置管理员账户打开 Microsoft Edge。请使用其他账户登录 在麒麟系统上安装Qwen3-TTS文字转语音 在麒麟系统上安装MaryTTS文字转语音 FTP上传Linux/Unix文件系统权限的修改方法 麒麟系统Kylin Linux Advanced Server 中安装 python3.10 将exe做成windows服务 java实现ftp上传 java实现TTS文字转语音wav (Jacob + SAPI) node.js和Next.js 编译部署说明
C语言开发中优雅的关闭程序并执行清理流程的写法
海乐学习 · 2026-06-17 · via 博客园 - 海乐学习

C语言开发中有下面这四种的退出程序的方法

1、Ctrl + C → 发送 SIGINT 信号

2、kill 进程PID(不带参数)→ 发送 SIGTERM   

3、kill -9 不执行任何清理

4、systemctl stop 停止服务 → 底层也是发送 SIGTERM

注:其中kill -9 是不执行任何清理的,所有在正常情况下不要使用

三种关闭操作对应的信号
1、Ctrl + C → 发送 SIGINT 信号
2、kill 进程PID(不带参数)→ 发送 SIGTERM
3、systemctl stop 停止服务 → 底层也是发送 SIGTERM
C 语言进程收到这两个信号,默认处理动作:立刻终止程序。

注意事项(生产环境必看)
信号处理函数内不能调用大量 IO / 复杂函数
ExitSys 内部需要主动关闭线程、zmq 上下文、释放硬件资源,否则会资源泄漏。
systemd 服务超时问题
如果 ExitSys 执行耗时较长(超过 systemd 默认 90 秒),服务会被强制发 SIGKILL(无法捕获,直接强杀)。
解决方案:
systemd 配置添加 TimeoutStopSec=120 延长停止等待时间;
ExitSys 内部所有关闭操作尽量缩短耗时。
kill -9 PID 无法捕获
任何代码都拦不住,进程瞬间销毁,不会执行任何清理,运维尽量不要用 kill -9。

实现原理

添加 SIGINT/SIGTERM 信号捕获 + 全局退出标记:
收到信号→标记置 1→while 循环结束→顺序执行 ExitSys、关闭日志,实现优雅退出程序

代码

主程序文件 ShPbxServer.c

#include <signal.h>          //退出信号
/*====================== 主函数 ======================*/
int main(void)
{
    // 重定向 stderr 以屏蔽 HMP 驱动的警告输出 (如 timer lost)
    //freopen("/dev/null", "w", stderr); //写这句退出信号就不能被补获到ExitSys()不能被执行

    sigset_t wait_sigset;
    siginfo_t sig_info;
    struct timespec zero_ts = {0, 0};
    int caught_sig;

    // 阻塞SIGINT/SIGTERM,子线程全部继承屏蔽,信号只留在主线程
    sigemptyset(&wait_sigset);
    sigaddset(&wait_sigset, SIGINT);
    sigaddset(&wait_sigset, SIGTERM);
    pthread_sigmask(SIG_BLOCK, &wait_sigset, NULL);

    // 仅屏蔽管道破裂信号,不处理退出信号
    signal(SIGPIPE, SIG_IGN);

    // 初始化日志
    Log_Init();
    Log_Write("=======================================");
    Log_Write("======== ShPbxServer v1.8 启动 ========");
    Log_Write("=======================================");

    if (InitSys() != 0)
    {
        Log_Write("[ShPbxServer][错误] 系统初始化失败");
        Log_Close();
        return -1;
    }
    Log_Write("[ShPbxServer][信息] 系统初始化成功,通道数:%d", nTotCh);
// ===================== 服务后台专用死循环(不退出、不阻塞、不占CPU) =====================
    //// 后台循环:只做非阻塞业务,不阻塞等待信号
    while (1)
    {// 后台死循环,微小sleep降低CPU,同时保证信号标记生效
        //sleep(1); // 每秒休眠一次,程序保持运行
        usleep(500000); // 500ms,比sleep(1)响应更快
        // 非阻塞检测是否收到退出信号
        // 正确传参:第二个参数 &sig_info 匹配 siginfo_t*
        caught_sig = sigtimedwait(&wait_sigset, &sig_info, &zero_ts);
        if (caught_sig > 0)
        {
            break;
        }
    }
    Log_Write("[ShPbxServer][信息] 收到停止信号,准备优雅退出...");
    ExitSys();
    Log_Close();// 关闭日志
    return 0;
}
/*====================== 系统退出 ======================*/
void ExitSys(void)
{
    //Log_Write("[ShPbxServer][信息] 正在关闭系统...");
// 1. 先关闭ZeroMQ(内部等待服务线程退出)
    Zmq_Cleanup();
    Log_Write("[ShPbxServer][信息] ZeroMQ 服务已关闭");

    // 2. 关闭音频推送ZeroMQ
    ZeroMQAudio_Cleanup();
    Log_Write("[ShPbxServer][信息] 音频流推送服务已关闭");

    // 3. 停止定时器线程
    if (bSetTime == 1)
    {
        KillTimer(Th);
        Log_Write("[ShPbxServer][信息] Timer定时器已停止");
    }

    // 4. 关闭CTI硬件,停止所有异步回调线程
    Api_SsmCloseCti();
    Log_Write("[ShPbxServer][信息] CTI 系统已关闭板卡SsmCloseCti()");
    usleep(200000);  // 延长等待,让回调函数全部执行完毕退出

    // 最后销毁互斥锁
    pthread_mutex_destroy(&g_TrkChMutex);
 
}