SDバスのプロパティを見る

SDバスのプロパティを見る

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/initCPU/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)イベントフィルタを設定します。

次のいずれかを実行してイベントを受信できます。

  1. sd_bus_wait(3)イベントが発生するまでブロックします。sd_bus_process(3)それらを処理するために。
  2. ループsd-busに接続sd-eventsd_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いくつかのメッセージを受け取ることができます。

関連情報