查看 sd-bus 属性

查看 sd-bus 属性

我有一个 GUI,可以在其中显示ActiveState多个 systemd 服务。

在 10Hz 时,我使用 sd-bus api 来查询每个服务,如下所示:

sd_bus* bus;
sd_bus_error err = SD_BUS_ERROR_NULL;
char* msg = 0;

sd_bus_default_system(&bus);

sd_bus_get_property_string(bus,
    "org.freedesktop.systemd1",
    "/org/freedesktop/systemd1/unit/foo_2eservice",
    "org.freedesktop.systemd1.Unit",
    "ActiveState",
    &err,
    &msg);

我的问题是,当我运行这段代码时,/sbin/init还是/lib/systemd/systemd-logind消耗了大约 50% 的 CPU。对代码进行分析显示sd_bus_get_property_string.我需要减少调用此函数的次数。

d-bus 接口的内省很有趣:

busctl introspect \
    org.freedesktop.systemd1 \
    /org/freedesktop/systemd1/unit/foo_2eservice \
    org.freedesktop.system1.Unit

NAME                             TYPE      SIGNATURE RESULT/VALUE   FLAGS
.Kill                            method    si        -     -
.Ref                             method    -         -     -
.Reload                          method    s         o     -
.ReloadOrRestart                 method    s         o     -
.ReloadOrTryRestart              method    s         o     -
.ResetFailed                     method    -         -     -
.Restart                         method    s         o     -
.SetProperties                   method    ba(sv)    -     -
.Start                           method    s         o     -
.Stop                            method    s         o     -
.TryRestart                      method    s         o     -
.Unref                           method    -         -     -
.ActiveEnterTimestamp            property  t         0     emits-change
.ActiveEnterTimestampMonotonic   property  t         0     emits-change
.ActiveExitTimestamp             property  t         0     emits-change
.ActiveExitTimestampMonotonic    property  t         0     emits-change
.ActiveState                     property  s         "inactive"   emits-change
...

这告诉我 ActiveState 属性发出变化

我如何获取文件描述符,或进入事件循环以接收该更改?


D-总线规范表明org.freedesktop.DBus.Properties.PropertiesChanged当属性改变时 systemd 会发出一个信号。我想我需要弄清楚如何订阅该信号。

答案1

我不允许添加评论,因此我扩展了@Stewart 的答案。要启用信号,必须订阅给他们:

Subscribe() 可以发送大多数总线信号。对信号感兴趣的客户端需要调用此方法。仅当至少一个客户端调用此方法时才会发出信号。

如果你想知道为什么sd_bus_wait再也没有回来,那么你错过了类似的东西

sd_bus_call_method(bus,
                   "org.freedesktop.systemd1",
                   "/org/freedesktop/systemd1",
                   "org.freedesktop.systemd1.Manager",
                   "Subscribe",
                   &error,
                   NULL,
                   NULL);

答案2

答案是使用sd_bus_match_signal(3)设置事件过滤器。

您可以通过执行以下操作之一来侦听事件:

  1. sd_bus_wait(3)阻塞直到事件发生并且sd_bus_process(3)来处理它们。
  2. 连接sd-bussd-event循环sd_bus_attach_event(3)(您可能还需要设置 sd-event 循环)。
  3. 使用sd_bus_get_fd(3),sd_bus_get_events(3)sd_bus_get_timeout(3)将 sd-bus 连接到您自己的事件循环。

下面是一个简短的 C 示例,说明了如何执行此操作:

/* gcc main.c -lsystemd */

#include <systemd/sd-bus.h>
#include <stdio.h>
#include <stdlib.h>

static inline const char *strna(const char *s) {
        return s ?: "n/a";
}

int message_callback(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) {
        printf("callback: path=%s interface=%s member=%s\n", 
                strna(sd_bus_message_get_path(m)), 
                strna(sd_bus_message_get_interface(m)), 
                strna(sd_bus_message_get_member(m))
        );
        return 0;
}

int main() {
        sd_bus* bus = NULL;
        sd_bus_error err = SD_BUS_ERROR_NULL;
        char* msg = NULL;
        void* userdata = NULL;

        sd_bus_default_system(&bus);

        sd_bus_match_signal(
                bus,                                             /* bus */
                NULL,                                            /* slot */
                NULL,                                            /* sender */
                "/org/freedesktop/systemd1/unit/foo_2eservice",  /* path */
                "org.freedesktop.DBus.Properties",               /* interface */
                "PropertiesChanged",                             /* member */
                NULL /*message_callback*/ ,                      /* callback */
                userdata
        );

        while( 1 ) { 
                sd_bus_wait(bus, UINT64_MAX);
                while ( sd_bus_process(bus, NULL) ) {  }

                sd_bus_get_property_string(
                        bus,                                             /* bus */
                        "org.freedesktop.systemd1",                      /* destination */
                        "/org/freedesktop/systemd1/unit/foo_2eservice", /* path */
                        "org.freedesktop.systemd1.Unit",                 /* interface */
                        "ActiveState",                                   /* member */
                        &err, 
                        &msg);

                printf("New state: %s\n", msg);
                free(msg);

        }

        sd_bus_error_free(&err);
        sd_bus_message_unref(ret);
        sd_bus_unref(bus);
        return 0;
}

我已经注释掉了回调机制。

请注意,您会收到一条有关更改的消息任何单位的属性。因此,如果您执行类似的操作systemctl stop,您可能会收到几条消息。

相关内容