ActiveState
複数のシステムサービスを表示するGUIがあります。
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
CPU/lib/systemd/systemd-logind
の約50%を消費することです。コードを分析すると、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() はほとんどのバス信号を送信できます。信号に興味があるクライアントはこのメソッドを呼び出す必要があります。シグナルは、1つ以上のクライアントがこのメソッドを呼び出すときにのみ放出されます。
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
いくつかのメッセージを受け取ることができます。