对D-Bus的良好理解 - Linux中的IPC机制
D-BUS是IPC(进程间通信)机制,可帮助应用程序互相通信。
D-Bus(Desktop Bus)是一个简单的IPC,作为Freedesktop项目的一部分开发。
它提供了各种应用程序的抽象层,以暴露其功能和可能性。
如果要利用应用程序的某些功能来使另一个程序执行特定任务,可以通过使进程D-Bus感知来轻松实现它。
一旦应用程序进行D-Bus兼容,就无需重新编译或者嵌入其中的代码,以使其与其他应用程序通信。
关于D-Bus真的很酷的一件事是它有助于开发人员编写代码,以便以他们选择的语言编写任何D-BUS的应用程序。
目前,C/C++,GLIB,Java,Python,Perl,Ruby等提供D-Bus绑定。
D-Bus是一个消息总线系统,一种用于彼此交换的简单方法,D-Bus提供系统和会话守护进程。
系统守护程序在系统启动级别启动,主要用于硬件事件,而在用户登录桌面环境时会出现会话守护程序,并且它用于桌面应用程序彼此连接。
注意:DBUS开发人员始终建议使用DBUS绑定库,例如DBUS-GLIB或者DBUS-QT,而不是直接使用DBUS API,他们表示DBUS API尚未冻结并直接使用此API程序员正在注册一些痛苦,在我看来,为了清楚地了解任何DBUS绑定库 ,它是一个非常好的主意潜入DBUS低级编程,请记住,我们将在这里使用的是DBUS API的琐碎部分。
D-Bus Internals
D-Bus是一个在后台运行的服务守护程序。
我们使用总线守护程序与应用程序及其功能进行交互。
总线守护程序转发并从应用程序接收消息。
有两种类型的总线守护进程:会议列和系统信。
在开始执行任何代码之前,有一些术语应该熟悉:
DBUS连接:
DbusConnection是用于通过指定DBUS_BUS_SYSTEM或者使用DBUS_BUS_SESSESSION指定DBUS_BUS_SYERTEM或者会话总线守护程序来打开与守护程序的连接,通过DBUS_BUS_SESSESSESSECTION来打开连接。
DBus消息:
这只是两个过程之间的消息,所有DBUS互通使用DBUS消息完成,这些消息可以具有以下类型,方法调用,方法返回,信号和错误。
DBUSMessage结构可以通过添加布尔整数,实数,字符串,阵列,...对邮件进行参数。
小路:
是远程对象,示例/org/freedesktop/dbus的路径。
界面:
是与给定对象上的界面进行交谈。
信号:
这是一个DBus消息,用于发出信号发射。
方法调用:
它是用于在远程对象上调用方法的DBus消息。
DBUS错误:
dbusError是包含通过调用DBUS方法而发生的错误代码的结构。
获得公共汽车连接:
DBusConnection *connection; DBusError error; dbus_error_init(&error); /* Initialize the error structure */ connection = dbus_bus_get(DBUS_BUS_SESSION,&error); /* Or DBUS_BUS_SYSTEM */ if ( dbus_error_is_set(&error) ) { printf("Error connecting to the daemon bus: %s",error.message); dbus_error_free(&error); }
目前,我们已经了解了DBUS的基本概念,接下来我们将通过示例学习基于DBUS的应用程序的内部详细信息。
例1:保留总线名称
只是为了更熟悉DBus编程,在这个例子中,我们将看到我们如何为我们的小应用程序保留公共汽车名称。
DBUS适用于总线名称存在一些限制,它们是有效的UTF-8字符串,必须至少有一个"。
"它将元素名称分开,每个元素必须包含至少一个字符,示例"org.freedesktop"。
,对于完整列表,请阅读DBUS规范部分总线名称。
#include <stdio.h> #include <dbus/dbus.h> int main() { DBusConnection *connection; DBusError error; char *name = "org.share.linux"; dbus_error_init(&error); connection = dbus_bus_get(DBUS_BUS_SESSION, &error); if ( dbus_error_is_set(&error) ) { printf("Error connecting to the daemon bus: %s",error.message); dbus_error_free(&error); return 1; } dbus_bool_t ret = dbus_bus_name_has_owner(connection,name,&error); if ( dbus_error_is_set(&error) ) { dbus_error_free(&error); printf("DBus Error: %s\n",error.message); return 1; } if ( ret == FALSE ) { printf("Bus name %s doesn't have an owner, reserving it...\n",name); int request_name_reply = dbus_bus_request_name( connection,name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); if ( dbus_error_is_set(&error) ) { dbus_error_free(&error); printf("Error requesting a bus name: %s\n",error.message); return 1; } if ( request_name_reply == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ) { printf("Bus name %s Successfully reserved!\n",name); return 0; } else { printf("Failed to reserve name %s\n",name); return 1; } } else /* if ret of method dbus_bus_name_has_owner is TRUE, then this is useful for detecting if your application is already running and had reserved a bus name unless somebody stole this name from you, so better to choose a correct bus name */ { printf("%s is already reserved\n", name); return 1; } return 0; }
我们使用了DBUS_NAME_FLAG_DO_NOT_QUEUE标志,DBUS在我们要预留的总线名称已经使用时不会排队我们。
对于DBUS_BUS_REQUEST_NAME的完整标志列表和返回代码请参阅DBUS API。
可以始终使用dbus_bus_release_name释放请求的总线名称。
要编译上面的代码,应安装DBUS Development包,根据发行版,此包的名称可能有所不同,但应该是libdbus-dev(在Slackware系统上的所有包装上都带来了他们的开发文件)。
然后使用以下命令编译代码:
gcc `pkg-config --libs --cflags dbus-1` example1.c -o example1
PKG-Config尝试查找DBUS-1.PC文件,通常此文件位于/usr/lib/pkgconfig中的其他文件,并且这些文件包含有关Link的库(IE)的信息。
示例2:连接两个桌面应用程序
在这个例子中,我们将使用DBUS连接两个桌面应用程序,一个人收听DBus消息,另一个发送DBus消息,但在开始之前,侦听器程序不应该刚刚开始和退出,它必须等待事件,所以我们有要找到一种方法来组织发送到我们的程序的事件,这是一个简单的解决方案是使用Glib的主事件循环,当使用它时,我们可以将我们的程序保持在睡眠模式,直到接收事件,发生另一个问题是这样的另一个问题我们如何将我们的总线连接与Glib主事件循环集成,这里有DBUS-GLIB,因此我们的微小程序也将依赖于DBUS-GLIB仅为一个调用,DBUS_CONNECTION_SETUP_WITH_G_MAIN,这次调用集成了GLIB主循环和DBUS总线事件。
一个问题提出其中如果我们想使用dbus,我们如何避免使用它的光纤绑定,答案并不简单,首先我们必须编写自己的循环事件,并将其与公交车事件集成,一个好开始是看DBUS源,因为它们在DBus/DBus-mainloop中有一个有用的代码,而是为了简化我们的工作,我们将使用DBUS-GLIB。
听取
在此程序中,我们将使用dbus_bus_add_match(dbusconnection *,const char *规则,dbusError *)为要接收的邮件添加匹配,规则字符串具有特定格式,请参阅DBUS匹配规则以获取完整详细信息。
#include <stdio.h> #include <dbus/dbus.h> #include <dbus/dbus-glib.h> #include <glib.h> static DBusHandlerResult dbus_filter (DBusConnection *connection, DBusMessage *message, void *user_data) { if ( dbus_message_is_signal(message,"org.share.linux","Customize" ) ) { printf("Message cutomize received\n"); return DBUS_HANDLER_RESULT_HANDLED; } if ( dbus_message_is_signal(message,"org.share.linux","Quit" ) ) { printf("Message quit received\n"); GMainLoop *loop = (GMainLoop*) user_data; g_main_loop_quit(loop); return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } int main() { DBusConnection *connection; DBusError error; /* glib main loop */ GMainLoop *loop; loop = g_main_loop_new(NULL,FALSE); dbus_error_init(&error); connection = dbus_bus_get(DBUS_BUS_SESSION, &error); if ( dbus_error_is_set(&error) ) { printf("Error connecting to the daemon bus: %s",error.message); dbus_error_free(&error); return 1; } dbus_bus_add_match (connection, "type='signal',interface='org.share.linux'",NULL); dbus_connection_add_filter (connection, dbus_filter, loop, NULL); /* dbus-glib call */ dbus_connection_setup_with_g_main(connection,NULL); /* run glib main loop */ g_main_loop_run(loop); return 0; }
send.c.
#include <stdio.h> #include <dbus/dbus.h> static void send_config(DBusConnection *connection) { DBusMessage *message; message = dbus_message_new_signal ("/org/share/linux", "org.share.linux", "Config"); /* Send the signal */ dbus_connection_send (connection, message, NULL); dbus_message_unref (message); } static void send_quit (DBusConnection *connection) { DBusMessage *message; message = dbus_message_new_signal ("/org/share/linux", "org.share.linux", "Quit"); /* Send the signal */ dbus_connection_send (connection, message, NULL); dbus_message_unref (message); } int main (int argc, char **argv) { DBusConnection *connection; DBusError error; dbus_error_init (&error); connection = dbus_bus_get (DBUS_BUS_SESSION, &error); if (!connection) { printf ("Failed to connect to the D-BUS daemon: %s", error.message); dbus_error_free (&error); return 1; } if ( argc == 1 ) { return 0; } int i; for ( i = 1; i < argc; i++) { if (!strcmp(argv[i],"-c") ) { send_config(connection); } else if ( !strcmp(argv[i],"-q") ) { send_quit(connection); } } return 0; }
要编译运行以下命令:
gcc `pkg-config --libs --cflags dbus-1 glib-2.0 dbus-glib-1` listen.c -o listen gcc `pkg-config --libs --cflags dbus-1` send.c -o send
示例3:DBUS服务
消息总线可以代表其他应用程序启动应用程序(服务),应用程序要求DBUS通过其名称启动服务,通常名称应该是org.freedesktop.textEditor。
为了使DBUS找到对应于特定名称的可执行文件,总线守护程序查找通常安装在/usr/share/dbus-1 /服务中的服务说明文件,并且它们在其扩展名中具有.service(所有Linux Distr我知道他们使用此前缀来安装DBUS服务文件,作为服务文件的示例。
DBUS服务文件示例:
[D-BUS Service] Name=org.share.linux Exec=path to the executable.
我们将写两个程序,一个是我们想要开始的服务,另一个是激活此服务的应用程序
share-linux-serivce-example.c
#include <stdio.h> #include <dbus/dbus.h> int main() { DBusConnection *connection; DBusError error; dbus_error_init(&error); connection = dbus_bus_get(DBUS_BUS_STARTER, &error); /* DBUS_BUS_STARTER is the bus that started us */ /* Do something here to make sure that the application was successfully started by DBus * Example could be something like * FILE *tmp; * tmp = fopen("/tmp/share-linux-service.result", "w"); * fprintf(tmp,"share-linux service was started successfully"); * fclose(tmp); * / /* After that you have the service up, so you can do whetever you like */ dbus_connection_unref(connection); return 0; }
编译此示例使用DBUS-1参数进行PKG-CONFIG,我们需要在/usr/share/dbus-1/service中安装服务文件,名称它org.share.linux并编辑
对我们拥有服务示例二进制的位置的exec路径。
start-service.c.
#include <stdio.h> #include <dbus/dbus.h>int main() { DBusConnection *connection; DBusError error; DBusMessage *message; const char *service_name = "org.share.linux"; dbus_uint32_t flag; /* Currently this is not used by DBus, they say it is for futur expansion*/ dbus_bool_t result; dbus_error_init(&error); connection = dbus_bus_get(DBUS_BUS_SESSION, &error); if ( dbus_error_is_set(&error) ) { printf("Error getting dbus connection: %s\n",error.message); dbus_error_free(&error); dbus_connection_unref(connection); return 0; } message = dbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "StartServiceByName"); if ( !message ) { printf("Error creating DBus message\n"); dbus_connection_unref(connection); return 0; } dbus_message_set_no_reply(message, TRUE); /* We don't want to receive a reply */ /* Append the argument to the message, must ends with DBUS_TYPE_UINT32 */ dbus_message_append_args(message, DBUS_TYPE_STRING, &service_name, DBUS_TYPE_UINT32, &flag, DBUS_TYPE_INVALID); result = dbus_connection_send(connection, message, NULL); if ( result == TRUE ) { printf("Successfully activating the %s service\n",service_name); } else { printf("Failed to activate the %s service\n",service_name); } dbus_message_unref(message); dbus_connection_unref(connection); return 0; }
调试D-Bus
要调试基于D-BUS的应用程序,我们可以使用DBUS-MONITOR来检查分析事件。
或者,我们可以使用像D-FEE D-BUS调试器工具(由John Palmeri编写)这样的易于使用的调试工具。