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

如何通过DBus检测Linux系统触发的关机事件

如何通过DBus订阅系统关机信号执行异步任务(systemd服务/嵌入式Linux)

在嵌入式Linux的systemd服务场景下,要区分「服务正常停止」和「系统关机触发的服务停止」,并只在关机时执行异步任务,最可靠的方式是订阅systemd提供的DBus关机预备信号。

核心原理

systemd的org.freedesktop.login1.Manager接口会在系统进入关机/重启流程时发送PrepareForShutdown信号,这个信号携带一个布尔参数:true表示即将关机,false表示关机请求被取消。我们可以通过监听这个信号,触发对应的异步任务。

具体实现步骤

  1. 连接到System DBus
    登录管理服务login1运行在系统总线(system bus)上,而非会话总线(session bus),所以你的服务必须连接到system bus才能接收信号。

  2. 订阅信号匹配规则
    需要设置的信号匹配条件:

    • 接口:org.freedesktop.login1.Manager
    • 信号成员:PrepareForShutdown
    • 对象路径:/org/freedesktop/login1
    • 发送方:org.freedesktop.login1
  3. 实现信号回调逻辑
    当收到PrepareForShutdown(true)信号时,启动你的异步任务;如果是false,则可以终止正在执行的任务(如果需要)。

代码示例(GDBus,C语言)

#include <gio/gio.h>

static void on_shutdown_signal(GDBusConnection *connection,
                               const gchar *sender_name,
                               const gchar *object_path,
                               const gchar *interface_name,
                               const gchar *signal_name,
                               GVariant *parameters,
                               gpointer user_data) {
    gboolean is_shutting_down;
    g_variant_get(parameters, "(b)", &is_shutting_down);

    if (is_shutting_down) {
        // 启动你的异步任务,比如调用异步函数、启动线程等
        g_print("System is shutting down, starting cleanup tasks...\n");
    } else {
        // 可选:取消未完成的异步任务
        g_print("Shutdown cancelled, stopping cleanup tasks...\n");
    }
}

int main(int argc, char *argv[]) {
    GDBusConnection *conn;
    GError *error = NULL;
    guint signal_id;

    // 连接到system bus
    conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
    if (!conn) {
        g_printerr("Failed to connect to system bus: %s\n", error->message);
        g_error_free(error);
        return 1;
    }

    // 订阅PrepareForShutdown信号
    signal_id = g_dbus_connection_signal_subscribe(
        conn,
        "org.freedesktop.login1",          // 发送方名称
        "org.freedesktop.login1.Manager",  // 接口名称
        "PrepareForShutdown",              // 信号名称
        "/org/freedesktop/login1",         // 对象路径
        NULL,                              // 匹配规则(无)
        G_DBUS_SIGNAL_FLAGS_NONE,
        on_shutdown_signal,                // 回调函数
        NULL,                              // 用户数据
        NULL                               // 销毁函数
    );

    // 运行主事件循环(根据你的服务框架调整,比如Qt事件循环、GLib主循环)
    GMainLoop *loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(loop);

    // 清理资源
    g_dbus_connection_signal_unsubscribe(conn, signal_id);
    g_object_unref(conn);
    g_main_loop_unref(loop);

    return 0;
}

systemd服务配置注意事项

为了确保你的异步任务能在关机时顺利完成,需要在.service文件中添加以下配置:

[Service]
Type=dbus
BusName=your.service.bus.name  # 替换为你的服务的DBus总线名称(如果适用)
TimeoutStopSec=30  # 设置足够长的停止超时时间,确保异步任务完成
ExecStart=/path/to/your/service
  • TimeoutStopSec:默认可能只有几秒,需要根据你的任务耗时调整,避免systemd强制杀死进程。
  • 如果你的服务不是DBus类型,也可以用Type=simple,但要确保进程在收到SIGTERM后不会立即退出,而是等待异步任务完成后再退出。

为什么不用析构函数?

析构函数的执行时机无法区分「正常停止服务」和「系统关机」,而且异步任务需要依赖事件循环或线程继续运行,析构函数执行时进程可能已经进入退出流程,资源被回收,异步任务无法正常完成。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 00:07:28