对D-Bus的良好理解 - Linux中的IPC机制

时间:2020-03-05 15:24:46  来源:igfitidea点击:

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编写)这样的易于使用的调试工具。