DBUS
[DBUS] 예제: g_dbus_interface_skeleton_export 및 g_dbus_interface_skeleton_unexport
2025. 12. 18. 23:53반응형
예제 1. g_dbus_interface_skeleton_unexport 및 g_signal_handler_disconnect 이용
일반적으로는 g_dbus_interface_skeleton_unexport()를 호출하면 GObject의 참조 카운팅 시스템이 자동으로 정리를 처리하지만, 명시적으로 시그널 연결을 해제하는 것이 더 안전하다.
왜 명시적으로 해제해야 하나?
- 메모리 누수 방지: 순환 참조가 있을 경우 GObject가 자동으로 해제되지 않을 수 있음
- 명확한 리소스 관리: 코드의 의도가 명확해지고 디버깅이 쉬워짐
- 베스트 프랙티스: GLib/GObject 프로그래밍에서 권장되는 방식
- 예측 가능한 동작: 종료 시퀀스를 명확하게 제어 가능
실제로 많은 경우 GObject의 참조 카운팅이 자동으로 정리하지만, 복잡한 애플리케이션에서는 명시적인 해제가 더 안전하다.
#include <gio/gio.h>
#include <glib.h>
// D-Bus 인터페이스 XML 정의
static const gchar introspection_xml[] =
"<node>"
" <interface name='org.example.Calculator'>"
" <method name='Add'>"
" <arg type='i' name='a' direction='in'/>"
" <arg type='i' name='b' direction='in'/>"
" <arg type='i' name='result' direction='out'/>"
" </method>"
" <method name='Sub'>"
" <arg type='i' name='a' direction='in'/>"
" <arg type='i' name='b' direction='in'/>"
" <arg type='i' name='result' direction='out'/>"
" </method>"
" </interface>"
"</node>";
// Add 메서드 핸들러
static gboolean
on_handle_add(GDBusInterfaceSkeleton *skeleton,
GDBusMethodInvocation *invocation,
gint a,
gint b,
gpointer user_data)
{
gint result = a + b;
g_print("Add called: %d + %d = %d\n", a, b, result);
// 결과 반환
g_dbus_method_invocation_return_value(invocation,
g_variant_new("(i)", result));
return TRUE;
}
// Sub 메서드 핸들러
static gboolean
on_handle_sub(GDBusInterfaceSkeleton *skeleton,
GDBusMethodInvocation *invocation,
gint a,
gint b,
gpointer user_data)
{
gint result = a - b;
g_print("Sub called: %d - %d = %d\n", a, b, result);
// 결과 반환
g_dbus_method_invocation_return_value(invocation,
g_variant_new("(i)", result));
return TRUE;
}
// 시그널 핸들러 ID를 저장할 구조체
typedef struct {
gulong add_handler_id;
gulong sub_handler_id;
} SignalHandlers;
// 버스 획득 콜백
static void
on_bus_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
GDBusInterfaceSkeleton *skeleton;
GDBusNodeInfo *node_info;
SignalHandlers *handlers;
GError *error = NULL;
g_print("Bus acquired: %s\n", name);
// introspection 데이터 파싱
node_info = g_dbus_node_info_new_for_xml(introspection_xml, &error);
if (error) {
g_printerr("Error parsing introspection XML: %s\n", error->message);
g_error_free(error);
return;
}
// 인터페이스 스켈레톤 생성
skeleton = g_dbus_interface_skeleton_new(
g_dbus_node_info_lookup_interface(node_info, "org.example.Calculator"));
// 시그널 핸들러 구조체 생성
handlers = g_new0(SignalHandlers, 1);
// 시그널 연결 및 핸들러 ID 저장
handlers->add_handler_id = g_signal_connect(skeleton, "handle-add",
G_CALLBACK(on_handle_add), NULL);
handlers->sub_handler_id = g_signal_connect(skeleton, "handle-sub",
G_CALLBACK(on_handle_sub), NULL);
// 인터페이스 export
if (!g_dbus_interface_skeleton_export(skeleton,
connection,
"/org/example/Calculator",
&error)) {
g_printerr("Error exporting interface: %s\n", error->message);
g_error_free(error);
g_object_unref(skeleton);
g_free(handlers);
g_dbus_node_info_unref(node_info);
return;
}
g_print("Interface exported at /org/example/Calculator\n");
// user_data에 skeleton과 handlers 저장
g_object_set_data_full(G_OBJECT(connection), "skeleton",
skeleton, g_object_unref);
g_object_set_data_full(G_OBJECT(connection), "handlers",
handlers, g_free);
g_dbus_node_info_unref(node_info);
}
// 이름 획득 콜백
static void
on_name_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_print("Name acquired: %s\n", name);
}
// 이름 손실 콜백
static void
on_name_lost(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_print("Name lost: %s\n", name);
if (connection) {
GDBusInterfaceSkeleton *skeleton;
SignalHandlers *handlers;
skeleton = g_object_get_data(G_OBJECT(connection), "skeleton");
handlers = g_object_get_data(G_OBJECT(connection), "handlers");
if (skeleton && handlers) {
g_print("Disconnecting signal handlers...\n");
// 시그널 핸들러 연결 해제
g_signal_handler_disconnect(skeleton, handlers->add_handler_id);
g_signal_handler_disconnect(skeleton, handlers->sub_handler_id);
g_print("Unexporting interface...\n");
g_dbus_interface_skeleton_unexport(skeleton);
}
}
// 메인 루프 종료
GMainLoop *loop = (GMainLoop *)user_data;
if (loop) {
g_main_loop_quit(loop);
}
}
// SIGINT 핸들러
static gboolean
on_signal(gpointer user_data)
{
g_print("\nReceived signal, shutting down...\n");
GMainLoop *loop = (GMainLoop *)user_data;
g_main_loop_quit(loop);
return FALSE;
}
int main(int argc, char *argv[])
{
GMainLoop *loop;
guint owner_id;
loop = g_main_loop_new(NULL, FALSE);
// SIGINT 핸들러 설정
g_unix_signal_add(SIGINT, on_signal, loop);
// D-Bus 이름 소유
owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
"org.example.Calculator",
G_BUS_NAME_OWNER_FLAGS_NONE,
on_bus_acquired,
on_name_acquired,
on_name_lost,
loop,
NULL);
g_print("Calculator server started. Press Ctrl+C to exit.\n");
// 메인 루프 실행
g_main_loop_run(loop);
// 정리
g_bus_unown_name(owner_id);
g_main_loop_unref(loop);
g_print("Server stopped.\n");
return 0;
}
예제 2. g_signal_handlers_disconnect_by_func 이용
g_signal_handlers_disconnect_by_func() 동작 방식
이 함수는 다음 조건이 모두 일치하는 시그널 핸들러를 찾아 연결을 해제한다.
- 콜백 함수: G_CALLBACK(on_handle_add)
- user_data: NULL (연결 시 전달한 값과 일치해야 함)
만약 같은 함수를 여러 번 연결했다면 모두 해제되므로, 특정 핸들러만 해제하려면 g_signal_handler_disconnect()와 핸들러 ID를 사용해야 한다. 하지만 이 경우처럼 각 시그널에 하나의 핸들러만 연결했다면 g_signal_handlers_disconnect_by_func()가 더 깔끔한 선택이다.
#include <gio/gio.h>
#include <glib.h>
// D-Bus 인터페이스 XML 정의
static const gchar introspection_xml[] =
"<node>"
" <interface name='org.example.Calculator'>"
" <method name='Add'>"
" <arg type='i' name='a' direction='in'/>"
" <arg type='i' name='b' direction='in'/>"
" <arg type='i' name='result' direction='out'/>"
" </method>"
" <method name='Sub'>"
" <arg type='i' name='a' direction='in'/>"
" <arg type='i' name='b' direction='in'/>"
" <arg type='i' name='result' direction='out'/>"
" </method>"
" </interface>"
"</node>";
// Add 메서드 핸들러
static gboolean
on_handle_add(GDBusInterfaceSkeleton *skeleton,
GDBusMethodInvocation *invocation,
gint a,
gint b,
gpointer user_data)
{
gint result = a + b;
g_print("Add called: %d + %d = %d\n", a, b, result);
// 결과 반환
g_dbus_method_invocation_return_value(invocation,
g_variant_new("(i)", result));
return TRUE;
}
// Sub 메서드 핸들러
static gboolean
on_handle_sub(GDBusInterfaceSkeleton *skeleton,
GDBusMethodInvocation *invocation,
gint a,
gint b,
gpointer user_data)
{
gint result = a - b;
g_print("Sub called: %d - %d = %d\n", a, b, result);
// 결과 반환
g_dbus_method_invocation_return_value(invocation,
g_variant_new("(i)", result));
return TRUE;
}
// 버스 획득 콜백
static void
on_bus_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
GDBusInterfaceSkeleton *skeleton;
GDBusNodeInfo *node_info;
GError *error = NULL;
g_print("Bus acquired: %s\n", name);
// introspection 데이터 파싱
node_info = g_dbus_node_info_new_for_xml(introspection_xml, &error);
if (error) {
g_printerr("Error parsing introspection XML: %s\n", error->message);
g_error_free(error);
return;
}
// 인터페이스 스켈레톤 생성
skeleton = g_dbus_interface_skeleton_new(
g_dbus_node_info_lookup_interface(node_info, "org.example.Calculator"));
// 시그널 연결
g_signal_connect(skeleton, "handle-add",
G_CALLBACK(on_handle_add), NULL);
g_signal_connect(skeleton, "handle-sub",
G_CALLBACK(on_handle_sub), NULL);
// 인터페이스 export
if (!g_dbus_interface_skeleton_export(skeleton,
connection,
"/org/example/Calculator",
&error)) {
g_printerr("Error exporting interface: %s\n", error->message);
g_error_free(error);
g_object_unref(skeleton);
g_dbus_node_info_unref(node_info);
return;
}
g_print("Interface exported at /org/example/Calculator\n");
// user_data에 skeleton 저장 (나중에 unexport하기 위해)
g_object_set_data_full(G_OBJECT(connection), "skeleton",
skeleton, g_object_unref);
g_dbus_node_info_unref(node_info);
}
// 이름 획득 콜백
static void
on_name_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_print("Name acquired: %s\n", name);
}
// 이름 손실 콜백
static void
on_name_lost(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_print("Name lost: %s\n", name);
if (connection) {
GDBusInterfaceSkeleton *skeleton;
skeleton = g_object_get_data(G_OBJECT(connection), "skeleton");
if (skeleton) {
g_print("Disconnecting signal handlers...\n");
// 함수 포인터로 시그널 핸들러 연결 해제
g_signal_handlers_disconnect_by_func(skeleton,
G_CALLBACK(on_handle_add),
NULL);
g_signal_handlers_disconnect_by_func(skeleton,
G_CALLBACK(on_handle_sub),
NULL);
g_print("Unexporting interface...\n");
g_dbus_interface_skeleton_unexport(skeleton);
}
}
// 메인 루프 종료
GMainLoop *loop = (GMainLoop *)user_data;
if (loop) {
g_main_loop_quit(loop);
}
}
// SIGINT 핸들러
static gboolean
on_signal(gpointer user_data)
{
g_print("\nReceived signal, shutting down...\n");
GMainLoop *loop = (GMainLoop *)user_data;
g_main_loop_quit(loop);
return FALSE;
}
int main(int argc, char *argv[])
{
GMainLoop *loop;
guint owner_id;
loop = g_main_loop_new(NULL, FALSE);
// SIGINT 핸들러 설정
g_unix_signal_add(SIGINT, on_signal, loop);
// D-Bus 이름 소유
owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
"org.example.Calculator",
G_BUS_NAME_OWNER_FLAGS_NONE,
on_bus_acquired,
on_name_acquired,
on_name_lost,
loop,
NULL);
g_print("Calculator server started. Press Ctrl+C to exit.\n");
// 메인 루프 실행
g_main_loop_run(loop);
// 정리
g_bus_unown_name(owner_id);
g_main_loop_unref(loop);
g_print("Server stopped.\n");
return 0;
}반응형
'DBUS' 카테고리의 다른 글
| [DBUS] g_dbus_method_invocation_return_value 함수에 대한 고찰 (0) | 2025.12.23 |
|---|---|
| [DBUS] gdbus-codegen을 활용한 기본 예제 - 2. cpp파일 포함하여 빌드 (0) | 2025.11.26 |
| [DBUS] gdbus-codegen 명령어에 대해 (0) | 2025.11.26 |
| [DBUS] 에러해결: fatal error: gio/gunixfdlist.h: No such file or directory 17 | # include <gio/gunixfdlist.h> (0) | 2025.11.26 |
| [DBUS] Ubuntu에서 glib, gio 라이브러리 설치하기 (glib-2.0, gio-2.0) (0) | 2025.11.25 |
