我有一个 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)
设置事件过滤器。
您可以通过执行以下操作之一来侦听事件:
sd_bus_wait(3)
阻塞直到事件发生并且sd_bus_process(3)
来处理它们。- 连接
sd-bus
到sd-event
循环sd_bus_attach_event(3)
(您可能还需要设置 sd-event 循环)。 - 使用
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
,您可能会收到几条消息。