dbus-glib 详解

狂风中的少年 提交于 2019-12-06 07:18:48

Dbus-glib使用方法说明

一、背景介绍

Phoenix平台从安全的角度考虑,广泛的使用DBUS进行进程间通讯。

1.优点:

DBUS总线分为系统总线与会话总线两类,两者之前不能互相通信,所以任何应用程序不能欺骗系统事件,安全性很好。

2.缺点

l 直接使用Dbus标准接口调用很繁琐,且之前Phoenix平台没有统一的DBUS接口封装,各服务之间各写一套,不易维护也容易出错。

l 接受方法调用端、消息接收端等程序需要非阻塞式(阻塞式的无法多线程DBUS通讯)判断是否接收到DBUS信息,形如:

While(1)

{

dbus_connection_read_write();

                                                                        msg =dbus_connection_borrow_message(conn);

                        if (NULL == msg) {

                                                usleep(xxx);

continue;

}

}

如上所示,多个服务同时运行的情况下,会占用大量CPU时间片,之前就有测试报告应用程序压力运行单一操作的情况下,应用程序会由快跑慢。

                因此需要一个稳定可靠的DBUS调用封装,上层统一该封装接口进行DBUS通讯。

二、Dbus-glib介绍

Dbus-glib是GNU标准库,在Dbus接口上封装,方便上层服务与应用更好的使用。其形如一个DBUS代理服务器,由它进行所有DBUS消息的遍历与转发,服务端与消息发送端只需要向DBUSdeamon申请注册唯一的DBUSname 、绑定GOBJECT后,DBUSdeamon就会将申请连到到该DBUSname的DBUS信息转发给指定应用。

 

 

 

 

 

 

 

 

 

 

直接调用DBUS接口的构图如下:

 

 

 

 

 

 

 

 

 

 

 

 

使用Dbus-glib结构图如下:

 从上图显示,所有的需要进行DBUS相互通讯的程序都只与Dbus daemon进行通讯

l 函数调用流程:

服务端申请一个GObject,绑定以下信息:

Dbus name:A,

Dbus object:B

Dbus interface:C

Method :D

注册到dbus daemon中,其中D设置为回调函数

客户端向dbus daemon申请调用注册信息为

Dbus name:A,

Dbus object:B

Dbus interface:C

的D函数

dbus daemon收到客户端的消息后,查询是否存在该注册信息的回调函数,如果找不到daemon会产生错误消息,作为应答消息给客户端找到则且执行该回调函数,将结果返回给客户端。

 

l 消息发送流程:

1.消息发送端申请一个GObject,绑定以下信息:

Dbus name:A,

Dbus object:B

Dbus interface:C

signal :D

注册到dbus daemon中

 

2.消息接收端向dbus daemon申请绑定注册信息为

Dbus name:A,

Dbus object:B

Dbus interface:C

Signal 为D的消息回调函数

dbus daemon收到消息发送端发出的DBUS消息后,查询是否存在该消息的绑定回调函数,且执行该回调函数。

 

所以消息发送端是不知道谁是接收端的,这个也与DBUS底层接口实现方式不同

 

注意:bus daemon不对消息重新排序,如果发送了两条消息到同一个进程,他们将按照发送顺序接受到。接受进程并需要按照顺序发出应答消息,例如在多线程中处理这些消息,应答消息的发出是没有顺序的。消息都有一个序列号可以与应答消息进行配对。

三、通过Dbus-glib写一个服务端

dbus-glib定义向dbus daemon申请一个注册信息的形式为GObject(C语言)的对象。

 

1.写一个XML

首先,先学习怎么使用内置的xml文件自动创建出易于使用的dbus代理对象。如下的一个xml文件描述了了一个名为“HelloWorld”,输入参数为char*,输出参数为char*[]的被调用的函数。

 dbus的接口描述文件统一采用utf-8编码。type域数据类型定义如下:

a

ARRAY 数组

b

BOOLEAN 布尔值

d

DOUBLE IEEE 754双精度浮点数

g

SIGNATURE 类型签名

i

INT32 32位有符号整数

n

INT16 16位有符号整数

o

OBJECT_PATH 对象路径

q

UINT16 16位无符号整数

s

STRING 零结尾的UTF-8字符串

t

UINT64 64位无符号整数

u

UINT32 32位无符号整数

v

VARIANT 可以放任意数据类型的容器,数据中包含类型信息。例如glib中的GValue。

x

INT64 64位有符号整数

y

BYTE 8位无符号整数

()

定义结构时使用。例如"(i(ii))"

{}

定义键-值对时使用。例如"a{us}"

a表示数组,数组元素的类型由a后面的标记决定。例如:

"as"是字符串数组。

数组"a(i(ii))"的元素是一个结构。用括号将成员的类型括起来就表示结构了,结构可以嵌套。

数组"a{sv}"的元素是一个键-值对。"{sv}"表示键类型是字符串,值类型是VARIANT。

 

一个node可以有多个interface ,一个interface可以有多个methodsignal,上例只以简单的单函数来说明,如果多个函数可以写成:

<node name="/">

  <interfacename="xxxx">

    <methodname="xx">

      <argtype="s"/>

      <argtype="as" direction="out"/>

</method>

   <methodname="xxxx">

   </method>

  </interface>

</node>

 

2.通过dbus-binding-tool生成头文件

dbus-binding-tool安装:

sudo apt-get install libdbus-glib-1-dev

执行以下指令:

dbus-binding-tool --mode=glib-server --prefix=your_module_nameyour_server.xml > xxx_stub.h

dbus-binding-tool --mode=glib-server --prefix=org_tizen_motionapp motion-api-proxy.xml > motion-api_proxy_stub.h

 

"--prefix"参数定义了对象前缀。设对象前缀是$(prefix),则生成的DBusGObjectInfo结构变量名就是dbus_glib_$(prefix)_object_info。

const DBusGObjectInfo dbus_glib_org_tizen_motionapp_object_info

 

绑定文件会为接口方法定义回调函数。回调函数的名称是这样的:首先将xml中的方法名称转换到全部小写,下划线分隔的格式,然后增加前缀"$(prefix)_"

 

 

 

 

 

 

例如:如果xml中有方法SendMessage,绑定文件就会引用一个名称为$(prefix)_send_message的函数。

<method name="MotionRecogCreate">

      <arg name="result_return" type="i" direction="out" />

      <arg name="motion_handle" type="i" direction="out" />

</method>

 

绑定文件中的函数org_tizen_motionapp_motion_recog_create

 

如上例中,--prefix= some_object 生成的general_stub.h中,DBusGObjectInfo结构变量名dbus_glib_some_object_object_info,回调函数名为some_object_hello_world。

生成的general_stub.h不需要手动修改,直接使用。

 

3.创建对象

dbus-glib用GObject实现dbus对象,所以我们首先要实现一个对象,继承于GObject,以下说明请参考提供的示例demo代码。

3.1 定义对象

typedef struct SomeObject

{

  GObject parent;

}SomeObject;

 

typedef struct SomeObjectClass

{

  GObjectClass parent;

}SomeObjectClass;

 

在 GObject 中,类是两个结构体的组合,一个是实例结构体,另一个是类结构体,上例中SomeObject是实例结构体,SomeObjectClass是类结构体

 

命名为XXX、XXXClass形式.

 

3.2实现类类型的定义

G_DEFINE_TYPE(SomeObject, some_object,G_TYPE_OBJECT)

G_DEFINE_TYPE可以让 GObject 库的数据类型系统能够识别我们所定义的SomeObject 类类型,它接受三个参数,第一个参数是类名,即 SomeObject;第二个参数则是类的成员函数(面向对象术语称之为“方法”或“行为”)名称的前缀,例如some_object _get_type 函数即为 SomeObject 类的一个成员函数,“some_object” 是它的前缀;第三个参数则指明 SomeObject类类型的父类型为G_TYPE_OBJECT

 

3.3声明类的函数

GType some_object_get_type (void);

#define SOME_TYPE_OBJECT     (some_object_get_type ())

static void some_object_init(SomeObject *obj)

{

}

 

static void some_object_class_init(SomeObjectClass *klass)

{

}

 

some_object_get_type函数的作用是向GObject 库所提供的类型管理系统提供要注册的SomeObject类类型的相关信息,可以不实现,但必须要声明。

 

some_object_init 是类成员的构造函数

 

some_object_class_init 是类结构的构造函数,与类成员构造函数区别在于,该构造函数只在该类定义时运行一次,常用来进行消息信号的初始化等。而some_object_init则在创建成员时都会调用一次(如obj = g_object_new)

 

上例中通过G_DEFINE_TYPE(SomeObject, some_object, G_TYPE_OBJECT)第二个参数把类成员定为some_object,所以其成员函数名为:

some_object_get_type

some_object_init

some_object_class_init

所以如果G_DEFINE_TYPE第二个参数为“XXX”,则相应的成员函数就是XXX_get_type、XXX_init…

 

至此对象创建完成。

 

4.向dbusdeamon申请注册

 

g_type_init ();

dbus_g_object_type_install_info(SOME_TYPE_OBJECT, &dbus_glib_some_object_object_info);

mainloop = g_main_loop_new (NULL,FALSE);

dbus_g_object_type_install_info的作用是向dbus-glib登记对象信息,dbus_glib_some_object_object_info是哪来的?是由前面XML生成的头文件中指定:

 

const DBusGObjectInfo dbus_glib_some_object_object_info= {

}

 

g_main_loop_new用来申请创建一个主循环,接收DBUS消息,用于服务端、消息接收端

bus = dbus_g_bus_get (DBUS_BUS_SESSION,&error);

 if (!bus)

lose_gerror ("Couldn't connect to session bus", error);

 

申请一个会话总线

 bus_proxy = dbus_g_proxy_new_for_name (bus,"org.freedesktop.DBus",

                                                 "/org/freedesktop/DBus",

                                                 "org.freedesktop.DBus");

 

创建连接到dbusdaemon

org.freedesktop.DBus ----   dbus daemonDBUS名

/org/freedesktop/DBus ---  dbus daemon对象名

org.freedesktop.DBus  ---  dbusdaemon interface

 

 

 if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error,

                               G_TYPE_STRING,"org.designfu.SampleService",

                               G_TYPE_UINT, 0,

                               G_TYPE_INVALID,

                               G_TYPE_UINT, &request_name_result,

                               G_TYPE_INVALID))

lose_gerror ("Failed to acquire org.designfu.SampleService",error);

调用dbusdaemon的函数“RequestName”,申请一个DBUS名为“org.designfu.SampleService”的注册信息

 

 obj = g_object_new (SOME_TYPE_OBJECT, NULL);

 dbus_g_connection_register_g_object (bus, "/SomeObject",G_OBJECT (obj));

申请之前定义的一个对象SomeObject,将该对象与bus绑定,“/SomeObject”是method的顶层对象路径

 

 g_main_loop_run (mainloop);

进入loop循环,DBUS服务器完成,信息如下:

Dbus name: org.designfu.SampleService

Dbus object: /SomeObject

Dbus interface:test.method.Type

Method :HelloWorld

5.实现method

在general_stub.h中查询method真实名称,如

 

static const DBusGMethodInfo dbus_glib_some_object_methods[]= {

  { (GCallback)

 some_object_hello_world,dbus_glib_marshal_some_object_BOOLEAN__STRING_POINTER_POINTER, 0 },

};

即函数名为some_object_hello_world,函数的第一个输入参数固定为对象实例的指针,最后一个参数必定是GError**,中间为用户自定的参数,如本例中声明如下

gboolean

some_object_hello_world (SomeObject *obj, const char*hello_message, char ***ret, GError **error)

 

最后在代码中完成该函数实现。

 

注意:函数声明要写在#include"general_stub.h"之前,否则编绎不识别some_object_hello_world

 

以上这些跟对象相关的部分比较繁琐,不理解的情况下也可以把它当作公式一样记下来,只需要修改自定义的部分,其它照搬就可以了。

四、通过Dbus-glib写一个客户端

客户端实现较服务端简单,并不需要像DBUS底层库调用那样申请自身的DBUS名等。

1. dbus-binding-tool --mode=glib-client--prefix=org_tizen_motionapp motion-api-proxy.xml > motion_proxy_stub.h

2.

g_type_init ();

申请一个会话总线

  bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);

  if (!bus)

    lose_gerror ("Couldn't connect to session bus", error);

 

向dbus deamon申请连接到以下信息的DBUS总线上

  remote_object = dbus_g_proxy_new_for_name (bus,

                                                                                                                             "org.designfu.SampleService",

                                                                                                                             "/SomeObject",

                                                                                                                             " test.method.Type ");

 

 

 

 

3.调用MotionRecogCreate方法:

gboolean

org_tizen_tv_si_motion_recog_create (DBusGProxy *proxy, gint* OUT_result_return, gint* OUT_motion_handle, GError **error)

 

{

  return dbus_g_proxy_call (proxy, "MotionRecogCreate", error, G_TYPE_INVALID, G_TYPE_INT, OUT_result_return, G_TYPE_INT, OUT_motion_handle, G_TYPE_INVALID);

}

 

使用上述的方法较好

  if (!dbus_g_proxy_call (remote_object, "HelloWorld", &error,

                                                                          G_TYPE_STRING, "Hello from example-client.c!", G_TYPE_INVALID,

                                                                          G_TYPE_STRV, &reply_list, G_TYPE_INVALID))

lose_gerror ("Failed to complete HelloWorld", error);

 

使用完成后释放返回参数,此例中返回值是一个字符串数组

  g_strfreev (reply_list);

释放与目标DBUS的连接,结束

g_object_unref (G_OBJECT (remote_object));

 

 

五、消息发送与接收

消息发送端

消息发送与服务端实现基本相同,只需要在类结构构造函数中增加对信号的初始化。

XML文件中增加<signalname =“xxx” />,以下是一个同时具备接受函数调用与发送信号功能XML文件示例

 

代码中在类结构构造函数中初始化该信号:

static void

some_object_class_init (SomeObjectClass *klass)

{

  signals[0] =

    g_signal_new ("say_hi",

                                                  G_OBJECT_CLASS_TYPE (klass),

                 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,

                  0,

                  NULL, NULL,

                 g_cclosure_marshal_VOID__STRING,

                  G_TYPE_NONE, 1, G_TYPE_UINT);

}

g_signal_new中的其它参数在简单功能不会用到,我们只需要使用第一个最后两个参数

第一个参数是信号的名字,与XML文件匹配,此处注意,XML中是“SayHi”,在此处信号名会被转换为“say_hi”,规则基本就是大写转小写、单词间加下划线,且第一个字符必须是字母。所以如果之前信号名叫PMlevel的话,建议改名为PmLevel,或者将g_signal_new中写成“p_mlevel”,否则运行会报关连错误。XML中如果信号名为全小写,则不需要转换。

最后两个参数表示消息包含的数据个数数据类型,如上例中表示该消息包含一个UINT型的数据。

 

发送消息

Level = 1; 

g_signal_emit (obj, signals[0], 0, level);

 

所以消息发送端只将消息发送给dbusdaemon,而由dbus daemon查询谁对该消息有响应,则将该消息发送给指定的进程。

 

消息接收端

消息接收端与客户端实现也基本相同,区别在于消息接收端处于loop循环接收状态,且需要绑定接收消息后的回调函数。

g_type_init ();

mainloop = g_main_loop_new (NULL, FALSE);

bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);

if (!bus)

  lose_gerror ("Couldn'tconnect to session bus", error);

向dbusdeamon申请连接到以下信息的DBUS总线上,消息由该总线发出

  remote_object =dbus_g_proxy_new_for_name (bus,

                                                                                                                             "org.designfu.SampleService",

                                                                                                                             "/SomeObject",

                                                                                                                             "test.server");

设置接收到消息后的回调函数

  dbus_g_proxy_add_signal(remote_object, "SayHi", G_TYPE_UINT, G_TYPE_INVALID);

  dbus_g_proxy_connect_signal(remote_object, "SayHi", G_CALLBACK (receive_signal_handler),NULL,NULL);

进入LOOP

  g_main_loop_run (mainloop);

 

当收到SayHi的DBUS消息时,客户端自动执行回调函数receive_signal_handler。

 

demo已经将函数调用与消息收发写在了一起,流程为:

l  客户端定时2秒调用一次服务器端的函数HelloWord

l  服务端HelloWord发送消息SayHi,附带整型数据1

l  客户端收到消息后,消息回调函数打印出收到的数据

六、效率改进

以上几点大致说明了dbus-glib使用方法,但其在使用上还是有不方便的地方:

1 一个API要定义一个xml接口描述

2 数据封装非常复杂,非常不利于以后接口的扩展

为了克服上面的缺点,提高可扩展性和效率,可以这样做:
如果一个应用分为client,server两端的话,要高效率的实现client/server之间
的通信,可以采用如下方式:

改进一:定义一个通用的APIxml 接口描述

<?xml version="1.0" encoding="UTF-8" ?>

<node name = "/org/freedesktop/DBus/General_api">

  <interface name=" org.freedesktop.DBus.general_api">

    <method name=" client_request">

      <annotation name="org.pmonitor.DBus.GLib.request" value="_ client_request_cb"/>

      <arg type="i" name="action_id" direction="in" /> 

                                                <arg type="ay" name="input_garray" direction="in" /> 

                                                <arg type="ay" name="output_garray" direction="out" />

      <arg type="i" name="result" direction="out" />

    </method>

  </interface>

</node>

 

Ay表示字节的数组,其数据类型为GArray,这个通用的模板关键之处就是这个Garray, Garray本身是个容器,这个容器里面可以装任何东西。我们就是利用这个GArray来实现client与server之间数据的传递,无论想传递什么要的数据。

Client side:

gboolean proxy_func1 (void)

{

                        int api_id = 0;      //这个在不同的proxy_func里面可以有不同的值,主要是区分函数作用

                        GArray* in_array = NULL;

                        GArray* out_array = NULL; //在这里不用分配内存,放在server侧做内存分配

 

                        in_array = g_array_new(FALSE, FALSE, sizeof(guint8));

                        if (!in_array)

                                                return FALSE;

 

                        //把你自己的数据封装到in_array中,假设你的数据结构是your_strcut_t

 

                        your_struct_t my_own_data;

 

                        //fill my_own_data

                        ...

 

                        //放到in_array中,这很关键

                        g_array_append_vals(in_array, my_own_data, sizeof(your_strcut_t));

 

                        //调用general_proxy.h中的dbus接口

org_freedesktop_DBus_general_api_client_request (DBusGProxy *proxy, const gint IN_action_id, const GArray* IN_input_garray, GArray** OUT_output_garray, gint* OUT_result, GError **error)

                        //client_request(dbus_proxy, api_id, in_array, &out_array, .....); //通过dbus把数据从到server侧,server侧如何处理,看第四步;

 

                        //当sever返回数据后,从out_array中取出来就可以了

                        your_strcut_t* g_array_data = (your_strcut_t*)out_array->data;

                        .....

 

                        //free

                        if (in_array)

                                                g_free (in_array);

 

                        if (out_array)

                                                g_free (out_array);

 

                        ....

}

 

Server side:

实现Server端,主要是实现 general_stub.h 中的函数single_method_client_request();

//这个函数的参数很长,除了第一个参数是server对象外,其余的参数可以直接从

general_proxy.h对应的接口参数拷贝过来;应该这个函数和proxy的接口是一对!

single_method_client_request (DBusGProxy *proxy, const gint IN_action_id, const GArray* IN_input_garray, GArray** OUT_output_garray, gint* OUT_result, GError **error)

{

                        //根据id 区分 method

                        {

                                                *OUT_output_garray = g_array_new(FALSE, FALSE, sizeof(guint8));//在client侧没有分配内存,server这里一定要分配

                                               

                                                //卸下client侧传递过来的数据

                                                your_struct_t *p_data = (your_struct_t *)&g_array_index(IN_input_garray, your_struct_t, 0);

 

                                                //对卸下的数据进行处理,看你的程序做什么功能了:)

                                                //如果要传回数据到client侧,假设处理过的数据为: your_struct_t dealed_with_data

                                                g_array_append_vals(*OUT_output_garray, &dealed_with_data, sizeof(your_struct_t));

                                                return TRUE;//一定要返回TRUE,否则client侧收不到数据的;

                        }

}

 

 

 

 

 

 

 

额外的参数类型支持如下:

D-Bus type signature

Description

GType

C typedef

Free function

Notes

as

Array of strings

G_TYPE_STRV

char **

g_strfreev

 

v

Generic value container

G_TYPE_VALUE

GValue *

g_value_unset

The calling conventions for values expect that method callers have
allocated return values; see below.

数组类型集合。

D-Bus type signature

Description

GType

C typedef

Free function

Notes

ay

Array of bytes

DBUS_TYPE_G_BYTE_ARRAY

GArray *

g_array_free

 

au

Array of uint

DBUS_TYPE_G_UINT_ARRAY

GArray *

g_array_free

 

ai

Array of int

DBUS_TYPE_G_INT_ARRAY

GArray *

g_array_free

 

ax

Array of int64

DBUS_TYPE_G_INT64_ARRAY

GArray *

g_array_free

 

at

Array of uint64

DBUS_TYPE_G_UINT64_ARRAY

GArray *

g_array_free

 

ad

Array of double

DBUS_TYPE_G_DOUBLE_ARRAY

GArray *

g_array_free

 

ab

Array of boolean

DBUS_TYPE_G_BOOLEAN_ARRAY

GArray *

g_array_free

 

 

Hash表数组:

D-Bus type signature

Description

GType

C typedef

Free function

Notes

a{ss}

Dictionary mapping strings to strings

DBUS_TYPE_G_STRING_STRING_HASHTABLE

GHashTable *

g_hash_table_destroy

 

 

改进二:用dbus的工具函数生成proxy头文件

前面有说到使用dbus-binding-tool将XML脚本转换为stub.h供服务器使用

 dbus-binding-tool--mode=glib-server --prefix=your_module_name dbus_general.xml >general_stub.h

其实将--mode值修改为glib-client,生成proxy.h文件可供客户端使用,如:
dbus-binding-tool --mode=glib-client --prefix=your_module_name dbus_general.xml> general_proxy.h

打开general_proxy.h看到服务端提供的HelloWorld已经转为

test_server_hello_world(DBusGProxy *proxy, const char * IN_arg0, char *** OUT_arg1, GError **error);

客户端可以直接调用test_server_hello_world,而不再需要使用dbus_g_proxy_call来调用该函数。

general_proxy.h中的另两个函数声明涉及异步调用,暂未使用到,没有了解

phoenix中的pms与libpms就是使用统用API实现多函数调用的例子。

 

七、注意事项

1.向dbus deamon注册DBUS的名称是可以重复的:

dbus_g_proxy_call (bus_proxy, "RequestName",&error,

                                                  G_TYPE_STRING,"org.designfu.SampleService",

                                                  G_TYPE_UINT, 0,

                                                  G_TYPE_INVALID,

                                                  G_TYPE_UINT, &request_name_result,

                                                  G_TYPE_INVALID)

 

比如demo中的dbus_server程序可以连续运行多次,通过dbus-monitor–session可以看到其分配的实际Bus Names是不同的,比如一个是“1.5”,一个是“1.6”,因于“1.5”先创建,所以其处于dbus deamon同名队列的顶部,所以当客户端发起DBUS通讯时,就只有“1.5”有效,当“1.5”退出后,“1.6”才会进入DBUS通讯状态。

 

2.函数调用中的out参数,如果是服务端申请的内存空间,客户端在使用完后,要记得释放内存。

如g_new -----g_free等,具体数据类型不同,其对应的申请内存与释放内存的接口不同,详细请查看http://developer.gnome.org/glib/stable/

 

3.客户端等完成函数调用等,要关闭DBUS连接:

 

remote_object= dbus_g_proxy_new_for_name ();

  g_object_unref(G_OBJECT (remote_object));

 

dbus_g_bus_get()

dbus_g_connection_unref(bus);

 

4.服务器端被调函数(method)对应的函数返回值是gboolean类型,实际程序运行成功或失败要通过输出参数返回,不能通过函数返回。函数实现中一定要返回TRUE,否则dbusdaemon 不会向客户端回复函数调用结果。

八、多线程防冲突

以上demo中实现方法只是单线程实现dbus调用。如果多线程的情况下,以及库函数情况下,为确保不同线程使用不同的DBusConnection,在创建dbus总线时要注意使用关键字创建各自私有的总线:

 

Method客户端实现:

GMainContext*main_context = NULL;

main_context =g_main_context_new();//申请独立的context

/*不使用dbus_g_bus_get*/

bus = dbus_g_bus_get_private(DBUS_BUS_SESSION,main_context,&error);

dbus_g_connection_close(bus);//私有的总线连接要先close才能unref

dbus_g_connection_unref(bus);

g_main_context_unref(main_context);

 

消息接收端实现:

GMainContext*main_context = NULL;

 

main_context= g_main_context_new();//申请独立的context

mainloop= g_main_loop_new (main_context, FALSE);

g_main_loop_unref(mainloop);

g_main_context_unref(main_context);

以这种方式实现的消息接收端,在多进程接收相同消息的情况下,只有一个进程能够接收到该消息,如果要多进程都能接收到消息,实现如下:

  main_context = g_main_context_default();

  mainloop = g_main_loop_new (main_context,FALSE);

  mainloop = g_main_loop_new (NULL, FALSE);

                这两种方法都是使用系统默认的context与总线绑定。

 

               

注意:

1.      g_main_context_unref()要与g_main_context_new() 配合使用,如果申请的资源未释放,会导致文件句柄泄露。

2.      dbus_g_bus_get_private申请的私有总线连接在使用完成后,要使用dbus_g_connection_close先关闭连接后再释放资源dbus_g_connection_unref, 否则只调用dbus_g_connection_unref会报“私有连接无法关闭”,导致内存泄露。

 

dbus_g_connection_close目前glib库并未封装,需要自已封装一个,方法如下:

#define  _DBUS_POINTER_UNSHIFT(p) ((void*) (((char*)p)- sizeof (void*)))

#define DBUS_CONNECTION_FROM_G_CONNECTION(x)    ((DBusConnection*)_DBUS_POINTER_UNSHIFT(x))

 

voiddbus_g_connection_close( DBusConnection * connection )

{

        returndbus_connection_close(DBUS_CONNECTION_FROM_G_CONNECTION(connection));

}   

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!