00 D-Bus nedir
D-Bus, Linux'ta süreçler arası iletişim için standart haline gelmiş mesaj yönlendirme protokolüdür; gömülü sistemlerde kaçınılmaz çünkü systemd, BlueZ ve NetworkManager'ın tamamı D-Bus üzerinden konuşur.
D-Bus (Desktop Bus), 2002 yılında freedesktop.org bünyesinde Red Hat ve GNOME geliştiricileri tarafından standartlaştırıldı. Başlangıçta masaüstü uygulamaları arasında servis keşfi ve olay bildirimi sağlamak için tasarlandı; ancak zamanla Linux sisteminin omurgasına dönüştü. Bugün systemd, servis yönetimini tamamen D-Bus üzerinden yürütür.
Gömülü Linux'ta D-Bus'tan kaçınmak neredeyse imkânsızdır. BlueZ Bluetooth yığını, NetworkManager ağ konfigürasyonu, udisks2 depolama yönetimi ve PulseAudio ses altyapısı — hepsi D-Bus arayüzü açar. Bu daemon'larla etkileşime girmek için D-Bus protokolünü anlamak zorunludur.
IPC mekanizmaları karşılaştırması
| Mekanizma | Kapsam | Servis keşfi | Async event | Overhead |
|---|---|---|---|---|
| Unix pipe | Parent-child | Yok | Yok | Çok düşük |
| Unix socket | Tek makine | Manuel (path) | epoll/select | Düşük |
| Shared memory | Tek makine | Yok | Yok (poll gerekli) | Çok düşük |
| D-Bus | Tek makine | Otomatik (bus adı) | Signal mekanizması | Orta (daemon) |
| gRPC | Ağ | Manuel (host:port) | Server streaming | Düşük (binary) |
| MQTT | Ağ | Topic tabanlı | Subscribe | Düşük |
D-Bus daemon mimarisi
D-Bus, hub-and-spoke modelinde çalışır. Merkezdeki dbus-daemon süreci tüm mesajları yönlendirir. Hiçbir süreç doğrudan başka bir sürece bağlanmaz; her mesaj önce daemon'a gider, daemon hedef süreci bulur ve iletir.
Süreç A dbus-daemon Süreç B
│ │ │
│── connect ──────────────▶│ │
│◀─ unique name (:1.47) ───│ │
│ │◀── connect ───────────────│
│ │─── unique name (:1.91) ──▶│
│ │ │
│── method_call ──────────▶│ │
│ dest: org.example.Svc │── route ─────────────────▶│
│ │ │── dispatch
│ │◀── method_return ─────────│
│◀── method_return ────────│ │
Session bus ve system bus
System bus tüm kullanıcıların paylaştığı veriyoludur. Root olarak çalışan daemon'lar burada bulunur. Soket: /run/dbus/system_bus_socket. Gömülü sistemlerde birincil bus budur.
Session bus her kullanıcı oturumuna özeldir. Masaüstü uygulamaları bu bus'ı kullanır. $DBUS_SESSION_BUS_ADDRESS ortam değişkeniyle adreslenir. Gömülü headless sistemlerde genellikle yoktur.
# Kurulum
apt install dbus libdbus-1-dev
# D-Bus daemon durumu
systemctl status dbus
# System bus socket
ls -la /run/dbus/system_bus_socket
# srw-rw-rw- 1 root messagebus 0 ... /run/dbus/system_bus_socket
# Session bus adresi (desktop ortamında)
echo $DBUS_SESSION_BUS_ADDRESS
# unix:path=/run/user/1000/bus
Embedded Linux'ta hafif alternatifler
Modern systemd tabanlı sistemlerde dbus.service, dbus-daemon yerine dbus-broker çalıştırabilir. API tamamen uyumludur; socket yolu değişmez. systemctl status dbus.service ile hangi implementasyonun çalıştığını görebilirsiniz.
Bu bölümde
- D-Bus'ın kökeni ve gömülü Linux'taki rolü
- Pipe, socket, shared memory ve D-Bus karşılaştırması
- Hub-and-spoke daemon mimarisi: mesaj yönlendirme akışı
- System bus ve session bus ayrımı
- dbus-broker ve sd-bus: hafif embedded alternatifleri
01 Temel kavramlar
D-Bus'ın dört temel kavramı — bus name, object path, interface ve member — birlikte bir servis API'sini benzersiz şekilde adresler.
Bus name
Bus name, bir sürecin D-Bus üzerindeki kimliğidir. İki türü vardır:
org.freedesktop.NetworkManager. Servis başladığında talep eder; kapatıldığında serbest kalır.:1.42. Her bağlantı için tekil, değiştirilemez.Object path
Bir servis içindeki nesne hiyerarşisini belirtir. Unix dosya yoluna benzer sözdizimi kullanır:
# systemd — root manager nesnesi
/org/freedesktop/systemd1
# Belirli bir unit nesnesi
/org/freedesktop/systemd1/unit/ssh_2eservice
# NetworkManager — aktif bağlantı
/org/freedesktop/NetworkManager/ActiveConnection/1
# BlueZ — Bluetooth adapter
/org/bluez/hci0
# BlueZ — bağlı cihaz
/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF
Interface
Bir nesnenin sunduğu API grubuna verilen isim. Bir nesne birden fazla interface implemente edebilir. Her D-Bus nesnesinin implemente etmesi beklenen standart interface'ler vardır:
Introspect() metodu — nesnenin XML tanımını döner.Get(), Set(), GetAll() — property erişimi.GetManagedObjects() — alt nesne hiyerarşisini döner (BlueZ kullanır).Method, Signal, Property
| Tür | Yön | Yanıt | Kullanım |
|---|---|---|---|
| Method | Client → Service → Client | Var (method_return veya error) | İstek-yanıt, RPC çağrısı |
| Signal | Service → (tüm subscriber'lar) | Yok | Asenkron olay bildirimi, broadcast |
| Property | Get/Set via DBus.Properties | Var | Servis durum değişkenleri |
Tür sistemi (type signatures)
D-Bus mesajları self-describing'dir: her parametre bir type signature ile tanımlanır. Bu signature introspection XML'inde ve API dokümantasyonunda görünür.
| Type code | C karşılığı | Açıklama |
|---|---|---|
y | uint8_t | Byte |
b | int (0/1) | Boolean |
i | int32_t | 32-bit signed integer |
u | uint32_t | 32-bit unsigned integer |
t | uint64_t | 64-bit unsigned integer |
d | double | IEEE 754 double |
s | char* | UTF-8 string (null-terminated) |
o | char* | Object path |
v | variant | Dinamik tür — içinde başka bir tür |
a{sv} | GHashTable* | String-variant sözlük (en yaygın dict) |
as | char** | String dizisi |
(iu) | struct | int32 + uint32 tuple |
Bu bölümde
- Bus name: well-known (
org.freedesktop.NetworkManager) ve unique (:1.42) farkı - Object path: nesne hiyerarşisini adresleyen Unix-path benzeri sözdizimi
- Interface: metod ve sinyal koleksiyonu; standart interface'ler
- Method, signal ve property: üç etkileşim türü ve farkları
- D-Bus type signatures:
s,i,v,a{sv}gibi temel kodlar
02 Introspection ve gdbus
gdbus ve busctl araçlarıyla D-Bus servislerini canlı keşfedebilir, introspection XML'ini okuyabilir ve komut satırından method çağrısı yapabilirsiniz.
busctl ile servis listesi
# System bus üzerindeki tüm servisler
busctl list
# Çıktı (kısaltılmış):
# NAME PID PROCESS USER
# :1.1 1 systemd root
# :1.2 423 NetworkManager root
# org.freedesktop.NetworkManager 423 NetworkManager root
# org.freedesktop.systemd1 1 systemd root
# org.bluez 512 bluetoothd root
# Servis ağaç yapısı
busctl tree org.freedesktop.systemd1
# Belirli nesnenin introspection
busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1
gdbus ile introspection
# NetworkManager root nesnesini incele
gdbus introspect --system \
--dest org.freedesktop.NetworkManager \
--object-path /
# Çıktı (XML fragment):
# <node>
# <interface name="org.freedesktop.NetworkManager">
# <method name="GetDevices">
# <arg type="ao" name="devices" direction="out"/>
# </method>
# <signal name="StateChanged">
# <arg type="u" name="state"/>
# </signal>
# <property name="State" type="u" access="read"/>
# </interface>
# </node>
# Tüm nesneleri recursive listele
gdbus introspect --system \
--dest org.freedesktop.NetworkManager \
--object-path / \
--recurse
gdbus call ile method çağırma
# NetworkManager sürümünü sorgula
gdbus call --system \
--dest org.freedesktop.NetworkManager \
--object-path /org/freedesktop/NetworkManager \
--method org.freedesktop.DBus.Properties.Get \
"org.freedesktop.NetworkManager" "Version"
# (<'1.44.2'>,)
# systemd ListUnits çağrısı
gdbus call --system \
--dest org.freedesktop.systemd1 \
--object-path /org/freedesktop/systemd1 \
--method org.freedesktop.systemd1.Manager.ListUnits
gdbus monitor ile signal izleme
# NetworkManager'dan tüm sinyalleri izle
gdbus monitor --system \
--dest org.freedesktop.NetworkManager
# Çıktı:
# /org/freedesktop/NetworkManager: org.freedesktop.NetworkManager.StateChanged (uint32 70,)
# /org/freedesktop/NetworkManager/ActiveConnection/1: \
# org.freedesktop.DBus.Properties.PropertiesChanged ...
# Tüm system bus sinyalleri (çok verbose)
gdbus monitor --system
busctl call ve monitor
# busctl call: tür signature sonra argümanlar
busctl call org.freedesktop.hostname1 \
/org/freedesktop/hostname1 \
org.freedesktop.hostname1 \
GetHostname
# Property okuma
busctl get-property org.freedesktop.hostname1 \
/org/freedesktop/hostname1 \
org.freedesktop.hostname1 \
Hostname
# Gerçek zamanlı signal izleme
busctl monitor org.freedesktop.NetworkManager
# JSON formatında çıktı (scripting için kullanışlı)
busctl --json=short get-property \
org.freedesktop.systemd1 \
/org/freedesktop/systemd1 \
org.freedesktop.systemd1.Manager \
Version
dbus-send eski araçtır; bugün busctl tercih edilmelidir. busctl systemd ile birlikte gelir, JSON çıktı desteği sunar ve type signature sözdizimi daha okunaklıdır. dbus-send hâlâ bazı eski betiklerde görülebilir.
Bu bölümde
busctl list,busctl tree,busctl introspect— servis keşfigdbus introspect— XML introspection okumagdbus call— komut satırından method çağrısıgdbus monitor/busctl monitor— gerçek zamanlı signal izleme- busctl JSON çıktısı — scripting entegrasyonu
03 Python GDBus bağlaması
PyGObject kütüphanesi aracılığıyla GDBus Python API'si, D-Bus servislerine proxy nesneleri üzerinden tip-güvenli erişim sunar.
Bağlantı kurma
from gi.repository import Gio, GLib
# System bus bağlantısı (senkron)
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
print(f"Bağlantı UUID: {bus.get_guid()}")
# Session bus bağlantısı
session_bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
Proxy oluşturma ve method çağırma
from gi.repository import Gio, GLib
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
# NetworkManager proxy'si oluştur
proxy = Gio.DBusProxy.new_sync(
bus,
Gio.DBusProxyFlags.NONE,
None, # interface info
"org.freedesktop.NetworkManager", # bus name
"/org/freedesktop/NetworkManager", # object path
"org.freedesktop.NetworkManager", # interface
None
)
# Property okuma (DBus.Properties.Get)
version = proxy.get_cached_property("Version")
print(f"NetworkManager sürümü: {version.get_string()}")
# GetDevices method çağrısı
devices = proxy.call_sync(
"GetDevices",
None,
Gio.DBusCallFlags.NONE,
-1,
None
)
# devices: GLib.Variant("(ao)", ...) — object path dizisi
device_paths = devices.unpack()[0]
for path in device_paths:
print(f" Aygıt: {path}")
Signal bağlama
from gi.repository import Gio, GLib
def on_state_changed(proxy, sender, signal_name, params):
if signal_name == "StateChanged":
state = params.unpack()[0]
states = {
20: "asleep", 30: "disconnected",
40: "disconnecting", 50: "connecting",
60: "connected_local", 70: "connected_site",
80: "connected_global"
}
print(f"NM durum: {states.get(state, state)}")
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
proxy = Gio.DBusProxy.new_sync(
bus, Gio.DBusProxyFlags.NONE, None,
"org.freedesktop.NetworkManager",
"/org/freedesktop/NetworkManager",
"org.freedesktop.NetworkManager",
None
)
# g-signal: tüm sinyaller bu callback'e düşer
proxy.connect("g-signal", on_state_changed)
loop = GLib.MainLoop()
loop.run() # Ctrl+C ile durdur
Aktif bağlantı bilgisi alma
from gi.repository import Gio, GLib
def get_active_connections():
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
nm = Gio.DBusProxy.new_sync(
bus, Gio.DBusProxyFlags.NONE, None,
"org.freedesktop.NetworkManager",
"/org/freedesktop/NetworkManager",
"org.freedesktop.NetworkManager",
None
)
# ActiveConnections property: object path dizisi
active = nm.get_cached_property("ActiveConnections")
if not active:
print("Aktif bağlantı yok")
return
for path in active.unpack():
conn_proxy = Gio.DBusProxy.new_sync(
bus, Gio.DBusProxyFlags.NONE, None,
"org.freedesktop.NetworkManager",
path,
"org.freedesktop.NetworkManager.Connection.Active",
None
)
iface = conn_proxy.get_cached_property("Id")
state = conn_proxy.get_cached_property("State")
iptype = conn_proxy.get_cached_property("Type")
print(f" [{iptype.get_string()}] {iface.get_string()} — durum: {state.get_uint32()}")
get_active_connections()
Python'da D-Bus için iki ana kütüphane vardır: dbus-python (eski, GLib event loop gerektirir) ve PyGObject üzerinden gi.repository.Gio (modern, GDBus kullanır). Yeni projelerde gi.repository.Gio tercih edin. Kurulum: apt install python3-gi.
Bu bölümde
Gio.bus_get_sync()ile system/session bus bağlantısıGio.DBusProxy.new_sync()ile proxy oluşturmaproxy.call_sync()ile senkron method çağrısıproxy.connect('g-signal', callback)ile signal bağlama- NetworkManager aktif bağlantı bilgisi alma — gerçek kullanım örneği
04 C ile dbus-1 API
libdbus (dbus-1) low-level C API'si, D-Bus protokolüne doğrudan erişim sağlar; verbose olmakla birlikte daemon bağımlılığı olmadan kullanılabilir.
Bağlantı ve method çağrısı
#include <stdio.h>
#include <stdlib.h>
#include <dbus/dbus.h>
int main(void)
{
DBusError err;
DBusConnection *conn;
DBusMessage *msg, *reply;
char *version = NULL;
dbus_error_init(&err);
/* System bus'a bağlan */
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
if (dbus_error_is_set(&err)) {
fprintf(stderr, "Bağlantı hatası: %s\n", err.message);
dbus_error_free(&err);
return 1;
}
/* Method call mesajı oluştur */
msg = dbus_message_new_method_call(
"org.freedesktop.NetworkManager", /* destination */
"/org/freedesktop/NetworkManager", /* object path */
"org.freedesktop.DBus.Properties", /* interface */
"Get" /* method */
);
/* Argümanları ekle: interface adı + property adı */
const char *iface = "org.freedesktop.NetworkManager";
const char *prop = "Version";
dbus_message_append_args(
msg,
DBUS_TYPE_STRING, &iface,
DBUS_TYPE_STRING, &prop,
DBUS_TYPE_INVALID
);
/* Senkron çağrı (5 saniye timeout) */
reply = dbus_connection_send_with_reply_and_block(
conn, msg, 5000, &err
);
if (!reply) {
fprintf(stderr, "Yanıt hatası: %s\n", err.message);
dbus_error_free(&err);
goto cleanup;
}
/* Yanıttan variant içindeki string'i oku */
DBusMessageIter iter, sub;
dbus_message_iter_init(reply, &iter);
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT) {
dbus_message_iter_recurse(&iter, &sub);
if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
dbus_message_iter_get_basic(&sub, &version);
printf("NM Sürümü: %s\n", version);
}
}
cleanup:
if (reply) dbus_message_unref(reply);
dbus_message_unref(msg);
/* dbus_connection_unref(conn) — paylaşılan bağlantı, unref etme */
return 0;
}
Derleme
gcc dbus1_call.c -o dbus1_call \
$(pkg-config --cflags --libs dbus-1)
# Cross-compile (ARM)
arm-linux-gnueabihf-gcc dbus1_call.c -o dbus1_call \
$(pkg-config --cflags --libs dbus-1)
Argüman tipleri ve DBusMessageIter
const char*. dbus_message_iter_get_basic() ile const char* alınır.dbus_int32_t. Signed 32-bit integer.dbus_uint32_t. Unsigned 32-bit integer.dbus_bool_t. 0 veya 1.dbus_message_iter_recurse() ile girilir.dbus_message_iter_recurse() + dbus_message_iter_next() döngüsü ile girilir.dbus_bus_get() paylaşılan bir bağlantı döndürür. Bu bağlantıyı dbus_connection_unref() ile kapatmayın — referans sayısı sıfıra düştüğünde bağlantı kapatılır ve aynı süreçteki diğer D-Bus kullanıcıları etkilenir. Özel bağlantı için dbus_bus_get_private() kullanın.
Bu bölümde
dbus_bus_get(DBUS_BUS_SYSTEM)ile bağlantı kurmadbus_message_new_method_call()ve argüman eklemedbus_connection_send_with_reply_and_block()— senkron çağrıDBusMessageIterile yanıt ayrıştırma- Bellek yönetimi:
dbus_message_unref()
05 sd-bus (libsystemd)
sd-bus, libsystemd içinde gelen modern D-Bus implementasyonudur; dbus-1'e göre çok daha sade API sunar ve gömülü sistemlerde tercih edilen çözümdür.
sd-bus vs dbus-1 karşılaştırması
| Özellik | dbus-1 (libdbus) | sd-bus (libsystemd) |
|---|---|---|
| API karmaşıklığı | Yüksek (verbose iterator) | Düşük (tek satır call) |
| Performans | Orta | Daha iyi (direkt socket) |
| Bağımlılık | libdbus-1 | libsystemd (zaten var) |
| Async event loop | Manuel | sd-event entegrasyonu |
| Vtable API | Yok | Var (servis yazımı kolay) |
| Kdbus/AF_UNIX | Hayır | Evet (otomatik seçim) |
sd-bus ile method çağrısı
#include <stdio.h>
#include <systemd/sd-bus.h>
int main(void)
{
sd_bus *bus = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *reply = NULL;
const char *version;
int rc;
/* System bus'a bağlan */
rc = sd_bus_open_system(&bus);
if (rc < 0) {
fprintf(stderr, "Bağlantı hatası: %s\n", strerror(-rc));
return 1;
}
/* Property oku — tek satırda! */
rc = sd_bus_get_property(
bus,
"org.freedesktop.NetworkManager", /* destination */
"/org/freedesktop/NetworkManager", /* object path */
"org.freedesktop.NetworkManager", /* interface */
"Version", /* property */
&error,
&reply,
"s" /* type sig */
);
if (rc < 0) {
fprintf(stderr, "Property hatası: %s\n", error.message);
goto out;
}
sd_bus_message_read(reply, "s", &version);
printf("NM sürümü: %s\n", version);
out:
sd_bus_error_free(&error);
sd_bus_message_unref(reply);
sd_bus_unref(bus);
return rc < 0 ? 1 : 0;
}
sd_bus_call_method ile method çağrısı
/* systemd servisini yeniden başlat */
int restart_service(sd_bus *bus, const char *unit)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *reply = NULL;
int rc;
rc = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"RestartUnit",
&error,
&reply,
"ss", /* iki string argüman */
unit, /* "ssh.service" gibi */
"replace" /* mode */
);
if (rc < 0)
fprintf(stderr, "Hata: %s — %s\n",
error.name, error.message);
else {
const char *job_path;
sd_bus_message_read(reply, "o", &job_path);
printf("Job: %s\n", job_path);
}
sd_bus_error_free(&error);
sd_bus_message_unref(reply);
return rc;
}
Signal subscribe
/* Signal callback */
static int on_unit_new(sd_bus_message *m, void *userdata,
sd_bus_error *ret_err)
{
const char *id, *path;
sd_bus_message_read(m, "so", &id, &path);
printf("Yeni unit: %s — %s\n", id, path);
return 0;
}
/* Subscribe */
sd_bus_slot *slot = NULL;
rc = sd_bus_match_signal(
bus,
&slot,
"org.freedesktop.systemd1", /* sender */
"/org/freedesktop/systemd1", /* path */
"org.freedesktop.systemd1.Manager", /* interface */
"UnitNew", /* member */
on_unit_new,
NULL
);
/* Event loop */
while (1) {
rc = sd_bus_process(bus, NULL);
if (rc < 0) break;
if (rc > 0) continue; /* daha işlenecek mesaj var */
sd_bus_wait(bus, UINT64_MAX); /* yeni mesaj bekle */
}
CFLAGS := $(shell pkg-config --cflags libsystemd)
LDFLAGS := $(shell pkg-config --libs libsystemd)
sdbus_call: sdbus_call.c
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
Bu bölümde
sd_bus_open_system()/sd_bus_open_user()ile bağlantısd_bus_call_method()— tek satır method call, type-safesd_bus_get_property()— property okumasd_bus_match_signal()— signal subscribesd_bus_process()+sd_bus_wait()— basit event loop
06 Servis yazmak: method ve signal
sd-bus vtable API'si ile D-Bus üzerinden method, property ve signal sunan tam bir C servisi oluşturabilirsiniz.
Vtable tanımı
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <systemd/sd-bus.h>
#define SERVICE "com.example.TempSensor"
#define PATH "/com/example/TempSensor"
#define IFACE "com.example.TempSensor"
static int32_t g_temperature = 230; /* °C × 10 */
static sd_bus *g_bus = NULL;
/* GetTemperature method handler */
static int method_get_temperature(sd_bus_message *m, void *ud,
sd_bus_error *err)
{
printf("GetTemperature çağrıldı\n");
return sd_bus_reply_method_return(m, "i", g_temperature);
}
/* SetTemperature method handler */
static int method_set_temperature(sd_bus_message *m, void *ud,
sd_bus_error *err)
{
int32_t new_temp;
int rc = sd_bus_message_read(m, "i", &new_temp);
if (rc < 0)
return rc;
if (new_temp < -400 || new_temp > 1500) {
return sd_bus_error_setf(err,
SD_BUS_ERROR_INVALID_ARGS,
"Sıcaklık aralık dışı: %d", new_temp);
}
g_temperature = new_temp;
/* TemperatureChanged sinyali yayınla */
sd_bus_emit_signal(g_bus, PATH, IFACE,
"TemperatureChanged", "i", g_temperature);
return sd_bus_reply_method_return(m, "");
}
/* Property getter */
static int prop_get_temperature(sd_bus *bus, const char *path,
const char *interface, const char *property,
sd_bus_message *reply, void *ud, sd_bus_error *err)
{
return sd_bus_message_append(reply, "i", g_temperature);
}
/* Vtable — servisin tüm API tanımı */
static const sd_bus_vtable sensor_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("GetTemperature", "", "i",
method_get_temperature, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetTemperature", "i", "",
method_set_temperature, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_PROPERTY("Temperature", "i", prop_get_temperature,
0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_SIGNAL("TemperatureChanged", "i", 0),
SD_BUS_VTABLE_END
};
int main(void)
{
sd_bus_slot *slot = NULL;
int rc;
rc = sd_bus_open_system(&g_bus);
if (rc < 0) { fprintf(stderr, "Bus: %s\n", strerror(-rc)); return 1; }
/* Nesneyi vtable ile yayınla */
rc = sd_bus_add_object_vtable(g_bus, &slot, PATH, IFACE,
sensor_vtable, NULL);
if (rc < 0) { fprintf(stderr, "Vtable: %s\n", strerror(-rc)); goto out; }
/* Well-known isim talep et */
rc = sd_bus_request_name(g_bus, SERVICE, 0);
if (rc < 0) { fprintf(stderr, "Name: %s\n", strerror(-rc)); goto out; }
printf("Servis hazır: %s\n", SERVICE);
/* Event loop */
while (1) {
rc = sd_bus_process(g_bus, NULL);
if (rc < 0) break;
if (rc > 0) continue;
sd_bus_wait(g_bus, UINT64_MAX);
}
out:
sd_bus_slot_unref(slot);
sd_bus_unref(g_bus);
return rc < 0 ? 1 : 0;
}
Test
# Servisi arka planda başlat (policy dosyası gerekli, bkz. bölüm 07)
sudo ./temp_sensor_service &
# GetTemperature çağrısı
busctl call com.example.TempSensor \
/com/example/TempSensor \
com.example.TempSensor \
GetTemperature
# i 230
# SetTemperature çağrısı
busctl call com.example.TempSensor \
/com/example/TempSensor \
com.example.TempSensor \
SetTemperature i 280
# Property okuma
busctl get-property com.example.TempSensor \
/com/example/TempSensor \
com.example.TempSensor \
Temperature
# i 280
SD_BUS_METHOD() makrosunun ikinci parametresi giriş tip imzası, üçüncüsü çıkış tip imzasıdır. "" boş anlamına gelir. SD_BUS_VTABLE_UNPRIVILEGED flag'ı, root olmayan kullanıcıların bu metodu çağırmasına izin verir. Ayrıcalıklı işlemler için bu flag çıkarılır ve polkit entegrasyonu yapılır.
Bu bölümde
SD_BUS_VTABLE_START/SD_BUS_VTABLE_ENDile vtable yapısıSD_BUS_METHOD,SD_BUS_PROPERTY,SD_BUS_SIGNALmakrolarısd_bus_reply_method_return()ile yanıt göndermesd_bus_request_name()ile well-known isim kaydısd_bus_emit_signal()ile sinyal yayınlama
07 Güvenlik: policy dosyaları
D-Bus güvenliği XML policy dosyalarıyla yönetilir; sistem bus'ta servis açmak veya mesaj göndermek için bu dosyaların doğru yapılandırılması zorunludur.
Policy dosyası konumu ve yapısı
System bus policy dosyaları /etc/dbus-1/system.d/ dizinine yerleştirilir. Dosya adı genellikle bus adıyla eşleşir: com.example.TempSensor.conf.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Servis sahibi: yalnızca root çalıştırabilir -->
<policy user="root">
<allow own="com.example.TempSensor"/>
<allow send_destination="com.example.TempSensor"/>
<allow receive_sender="com.example.TempSensor"/>
</policy>
<!-- Varsayılan: tüm kullanıcılar GetTemperature çağırabilir -->
<policy context="default">
<allow send_destination="com.example.TempSensor"
send_interface="com.example.TempSensor"
send_member="GetTemperature"/>
<allow receive_sender="com.example.TempSensor"/>
</policy>
<!-- operator grubu: SetTemperature de çağırabilir -->
<policy group="operator">
<allow send_destination="com.example.TempSensor"
send_interface="com.example.TempSensor"
send_member="SetTemperature"/>
</policy>
</busconfig>
İzin türleri
<allow own="com.example.Foo"/>* wildcard.Policy yeniden yükleme ve systemd activation
# Policy dosyasını kopyala
sudo cp com.example.TempSensor.conf /etc/dbus-1/system.d/
sudo chmod 644 /etc/dbus-1/system.d/com.example.TempSensor.conf
# dbus-daemon'ı yeniden yükle (servis yeniden başlatılmaz)
sudo systemctl reload dbus
# Policy hata logları
journalctl -u dbus --since "1 min ago"
systemd D-Bus activation
Bir D-Bus servisini, istemci mesaj gönderdiğinde otomatik başlatmak için systemd activation kullanılır:
[D-BUS Service]
Name=com.example.TempSensor
Exec=/usr/bin/temp-sensor-service
User=root
SystemdService=com.example.TempSensor.service
[Unit]
Description=Temperature Sensor D-Bus Service
After=dbus.service
[Service]
Type=dbus
BusName=com.example.TempSensor
ExecStart=/usr/bin/temp-sensor-service
User=root
Restart=on-failure
[Install]
WantedBy=multi-user.target
Policy dosyasında <allow send_destination="*"/> veya <allow own="*"/> gibi wildcard izinler asla kullanmayın. Bu, herhangi bir sürecin herhangi bir servise mesaj göndermesine ya da var olan bir servis adını ele geçirmesine olanak tanır. Her zaman servis adlarını ve interface'leri açıkça belirtin.
Bu bölümde
- Policy XML formatı:
own,send_destination,send_interface,receive_sender - Kullanıcı, grup ve
context="default"bazlı izin katmanları - Policy yeniden yükleme:
systemctl reload dbus - systemd D-Bus activation:
[D-BUS Service]dosyası + systemd unit
08 Embedded'da D-Bus alternatifleri
dbus-daemon'ın performans maliyeti gömülü kaynak kısıtlı sistemlerde sorun yaratabilir; dbus-broker, kdbus ve domain-specific alternatiflerin doğru seçimi için karşılaştırmalı bir değerlendirme gereklidir.
dbus-daemon sorunları
Geleneksel dbus-daemon her mesaj için iki context switch gerektirir: gönderen → daemon → alıcı. Yüksek frekanslı mesajlaşmada (saniyede yüzlerce mesaj) bu gecikme birikir. Ayrıca daemon'ın fork-exec modeli başlangıç gecikmesi ekler.
| Çözüm | Latency | Bellek | Durum |
|---|---|---|---|
| dbus-daemon | ~100 µs | ~3 MB | Yaygın, stabil |
| dbus-broker | ~50 µs | ~1.5 MB | Fedora/RHEL varsayılanı |
| kdbus (kernel) | ~5 µs | Çok az | Mainline'da yok |
| sd-bus (direkt) | ~10 µs | ~0.5 MB | Daemon bypass mümkün |
dbus-broker
dbus-broker, routing mantığını basitleştirmek amacıyla sıfırdan yazılmıştır. Güvenlik policy'si ayrı bir süreç (dbus-broker-launch) tarafından yönetilir. API tamamen uyumludur — mevcut kod değiştirilmeden çalışır.
# dbus-broker kurulumu (Debian/Ubuntu)
apt install dbus-broker
# dbus.service'i dbus-broker ile değiştir
systemctl enable --now dbus-broker.service
systemctl disable dbus.service
# Çalışma kontrolü
systemctl status dbus-broker.service
busctl list # Hâlâ aynı çıktı
Domain-specific alternatifler
Ne zaman D-Bus kullanmalı
D-Bus'ı tercih edin: systemd zaten hedef sistemde varsa; BlueZ, NetworkManager veya udisks2 ile entegrasyon gerekiyorsa; servis keşfi ve introspection değerliyse; varolan D-Bus API'lerine bağlanılıyorsa. Yalnızca iki uygulama arasında yüksek frekanslı veri aktarımı için Unix socket veya shared memory daha uygun olabilir.
Bu bölümde
- dbus-daemon latency sorunu ve context switch maliyeti
- dbus-broker: daha hızlı routing, tam API uyumluluğu
- kdbus: kernel-space routing (mainline'da henüz yok)
- nanomsg, ZeroMQ, MQTT, Unix socket alternatifleri
- D-Bus seçim kriterleri: systemd bağımlılığı ve servis entegrasyonu
09 Pratik: BlueZ D-Bus API
BlueZ, Bluetooth yığınının tamamını D-Bus üzerinden açar; Python ile adapter yönetimi, cihaz tarama ve GATT characteristic okuma yapılabilir.
BlueZ nesne hiyerarşisi
# BlueZ nesne ağacını gör
busctl tree org.bluez
# Çıktı:
# └─ /org/bluez
# └─ /org/bluez/hci0
# ├─ /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF
# │ ├─ /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0001
# │ │ ├─ /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0001/char0002
# │ │ └─ /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0001/char0004
# Adapter interface'ini incele
busctl introspect org.bluez /org/bluez/hci0 \
org.bluez.Adapter1
Python ile Bluetooth cihaz tarama
import time
from gi.repository import Gio, GLib
BLUEZ_SERVICE = "org.bluez"
ADAPTER_PATH = "/org/bluez/hci0"
ADAPTER_IFACE = "org.bluez.Adapter1"
OBJ_MGR_IFACE = "org.freedesktop.DBus.ObjectManager"
def get_adapter(bus):
return Gio.DBusProxy.new_sync(
bus, Gio.DBusProxyFlags.NONE, None,
BLUEZ_SERVICE, ADAPTER_PATH, ADAPTER_IFACE, None
)
def on_interfaces_added(conn, sender, path, iface, signal, params, userdata):
obj_path, ifaces = params.unpack()
if "org.bluez.Device1" in ifaces:
dev = ifaces["org.bluez.Device1"]
name = dev.get("Name", "?")
addr = dev.get("Address", "?")
rssi = dev.get("RSSI", "?")
print(f" Yeni cihaz: {name} [{addr}] RSSI={rssi}")
def scan_bluetooth(duration_sec=10):
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
adapter = get_adapter(bus)
# InterfacesAdded sinyaline subscribe ol
bus.signal_subscribe(
BLUEZ_SERVICE,
OBJ_MGR_IFACE,
"InterfacesAdded",
None,
None,
Gio.DBusSignalFlags.NONE,
on_interfaces_added,
None
)
# Taramayı başlat
adapter.call_sync("StartDiscovery", None,
Gio.DBusCallFlags.NONE, -1, None)
print(f"Tarama başladı ({duration_sec}s)...")
loop = GLib.MainLoop()
GLib.timeout_add_seconds(duration_sec, loop.quit)
loop.run()
# Taramayı durdur
adapter.call_sync("StopDiscovery", None,
Gio.DBusCallFlags.NONE, -1, None)
print("Tarama tamamlandı.")
if __name__ == "__main__":
scan_bluetooth()
GATT characteristic okuma
from gi.repository import Gio, GLib
BLUEZ_SERVICE = "org.bluez"
GATT_CHAR_IFACE = "org.bluez.GattCharacteristic1"
def read_characteristic(char_path):
"""GATT characteristic değerini oku (Raw bytes döner)."""
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
char_proxy = Gio.DBusProxy.new_sync(
bus, Gio.DBusProxyFlags.NONE, None,
BLUEZ_SERVICE, char_path, GATT_CHAR_IFACE, None
)
# ReadValue metodunu çağır (options: boş dict)
result = char_proxy.call_sync(
"ReadValue",
GLib.Variant("(a{sv})", [{}]),
Gio.DBusCallFlags.NONE,
-1,
None
)
raw_bytes = bytes(result.unpack()[0])
print(f"Raw: {raw_bytes.hex()}")
return raw_bytes
def notify_characteristic(char_path):
"""GATT notification'ları dinle."""
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
char_proxy = Gio.DBusProxy.new_sync(
bus, Gio.DBusProxyFlags.NONE, None,
BLUEZ_SERVICE, char_path, GATT_CHAR_IFACE, None
)
def on_prop_changed(proxy, changed_props, invalidated):
props = changed_props.unpack()
if "Value" in props:
val = bytes(props["Value"])
print(f"Notification: {val.hex()}")
char_proxy.connect("g-properties-changed", on_prop_changed)
char_proxy.call_sync("StartNotify", None,
Gio.DBusCallFlags.NONE, -1, None)
loop = GLib.MainLoop()
loop.run()
# Kullanım örneği — BLE sıcaklık sensörü (UUID 2A6E)
# char_path = "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0001/char0002"
# read_characteristic(char_path)
GetManagedObjects ile tüm BlueZ nesnelerini listeleme
from gi.repository import Gio
def list_bluez_objects():
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
obj_mgr = Gio.DBusProxy.new_sync(
bus, Gio.DBusProxyFlags.NONE, None,
"org.bluez", "/",
"org.freedesktop.DBus.ObjectManager", None
)
result = obj_mgr.call_sync("GetManagedObjects", None,
Gio.DBusCallFlags.NONE, -1, None)
objects = result.unpack()[0]
for path, ifaces in objects.items():
if "org.bluez.Device1" in ifaces:
dev = ifaces["org.bluez.Device1"]
print(f"{path}")
print(f" Name: {dev.get('Name', 'N/A')}")
print(f" Address: {dev.get('Address', 'N/A')}")
print(f" Paired: {dev.get('Paired', False)}")
print(f" Connected: {dev.get('Connected', False)}")
list_bluez_objects()
BlueZ'i programatik kullanmak için bluetoothd'nin çalışması ve hedef adaptörün Powered: yes durumunda olması gerekir. busctl set-property org.bluez /org/bluez/hci0 org.bluez.Adapter1 Powered b true komutuyla adaptörü açabilirsiniz.
Bu bölümde
- BlueZ nesne hiyerarşisi:
Adapter1,Device1,GattCharacteristic1 InterfacesAddedsinyali ile yeni cihaz tespitiStartDiscovery/StopDiscoveryile tarama yönetimiGattCharacteristic1.ReadValue()ile GATT okumaStartNotify+g-properties-changedile GATT notificationGetManagedObjectsile tüm BlueZ nesne ağacını alma