Systemd

觀看 sd-bus 屬性

  • September 1, 2021

我有一個 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 屬性發出了一個 change

如何獲取文件描述符,或點擊事件循環以接收該更改?


D-Bus 規范建議 systemd 將在屬性更改時發出信號org.freedesktop.DBus.Properties.PropertiesChanged。我想我需要弄清楚如何訂閱該信號。

答案是用來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,您可能會收到幾條消息。

引用自:https://unix.stackexchange.com/questions/527283