You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CentOS 9系统下以systemctl服务运行的C++控制台应用如何实现停止检测与资源清理?

嘿,这个场景太常见了——把控制台程序转成systemd服务后,原来的交互式退出逻辑直接失效,毕竟服务是后台运行的,没人能按回车。核心解决思路是让你的C++程序监听systemd发送的终止信号,在收到信号后优雅地完成资源清理再退出。

先搞懂systemd会发什么信号

当你执行systemctl stop your-service时,systemd默认会先给进程发SIGTERM信号,这是一个“友好”的终止通知,程序可以捕获它并做清理。如果程序在指定时间内没退出,systemd才会发SIGKILL(这个信号无法捕获,会直接强制杀死进程)。另外,像systemctl reload会发SIGHUP,你也可以一起处理。

C++程序里捕获信号的实现

推荐用sigaction()来注册信号处理函数(比老式的signal()更可靠)。核心思路是:在信号处理函数里设置一个退出标志,然后主循环检查这个标志,一旦触发就执行清理逻辑并退出。

下面是一个完整的示例代码:

#include <atomic>
#include <signal.h>
#include <unistd.h>
#include <fstream>
#include <iostream>

// 用原子变量确保多线程环境下的线程安全
std::atomic<bool> g_should_exit(false);

// 信号处理函数:只做最基础的标志设置,避免调用非异步安全函数
void signal_handler(int signum) {
    g_should_exit = true;
    // 这里可以用write()输出日志(write是异步安全的),不要用cout/printf
    const char* msg = "Received termination signal, preparing to exit...\n";
    write(STDOUT_FILENO, msg, sizeof(msg)-1);
}

int main() {
    // 配置信号处理
    struct sigaction sa;
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask); // 不阻塞其他信号
    sa.sa_flags = 0;

    // 捕获SIGTERM(systemd stop默认信号)和SIGHUP(重载信号)
    sigaction(SIGTERM, &sa, nullptr);
    sigaction(SIGHUP, &sa, nullptr);

    // 模拟你的业务资源:比如打开日志文件
    std::ofstream log_file("/var/log/my_app.log", std::ios::app);
    if (!log_file.is_open()) {
        const char* err = "Failed to open log file\n";
        write(STDERR_FILENO, err, sizeof(err)-1);
        return 1;
    }

    // 主业务循环
    while (!g_should_exit) {
        // 这里写你的程序核心逻辑
        log_file << "Application is running..." << std::endl;
        sleep(2);

        // 保留原有的控制台退出逻辑(可选)
        if (std::cin.peek() == '\n') {
            std::cin.ignore();
            g_should_exit = true;
        }
    }

    // 资源清理阶段:关闭文件、释放内存、断开连接等
    log_file << "Cleaning up resources..." << std::endl;
    log_file.close();

    const char* exit_msg = "Application exited gracefully\n";
    write(STDOUT_FILENO, exit_msg, sizeof(exit_msg)-1);

    return 0;
}

配合systemd服务文件的优化

要让systemd更好地管理你的服务,需要创建一个.service文件,比如/etc/systemd/system/my-app.service:

[Unit]
Description=My C++ Background Service
After=network.target  # 如果依赖网络,加上这个

[Service]
Type=simple
ExecStart=/usr/local/bin/my-app  # 替换成你的程序路径
User=non-root-user  # 尽量用非root用户运行,提升安全性
Group=non-root-group
TimeoutStopSec=30  # 给程序30秒时间完成清理,按需调整
Restart=on-failure  # 可选:程序崩溃时自动重启

[Install]
WantedBy=multi-user.target

然后执行以下命令启用服务:

sudo systemctl daemon-reload
sudo systemctl enable my-app.service
sudo systemctl start my-app.service

关键注意事项

  1. 信号处理函数要异步安全:绝对不要在信号处理函数里调用cout、printf或者C++标准库的大部分函数,这些不是异步安全的,可能导致程序崩溃。只做标志设置或用write()写日志。
  2. 多线程环境要注意同步:如果你的程序是多线程的,g_should_exit必须是原子变量,或者用互斥锁保护,避免竞态条件。
  3. 测试清理逻辑:执行systemctl stop my-app.service后,用journalctl -u my-app.service -f查看日志,确认清理代码是否执行。
  4. 避免SIGKILL:确保TimeoutStopSec足够长,让程序能完成清理,不然systemd会强制杀死进程,导致资源泄漏。

内容的提问来源于stack exchange,提问作者Boppity Bop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 17:43:11