




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Dbus-glib使用方法说明一、背景介绍Phoenix平台从安全的角度考虑,广泛的使用DBUS进行进程间通讯。1.优点:DBUS总线分为系统总线与会话总线两类,两者之前不能互相通信,所以任何应用程序不能欺骗系统事件,安全性很好。2.缺点直接使用Dbus标准接口调用很繁琐,且之前Phoenix平台没有统一的DBUS接口封装,各服务之间各写一套,不易维护也容易出错。接受方法调用端、消息接收端等程序需要非阻塞式(阻塞式的无法多线程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相互通讯的程序都只与Dbusdaemon进行通讯。函数调用流程:服务端申请一个GObject,绑定以下信息:Dbusname:A,Dbusobject:BDbusinterface:CMethod:D注册到dbusdaemon中,其中D设置为回调函数客户端向dbusdaemon申请调用注册信息为Dbusname:A,Dbusobject:BDbusinterface:C的D函数dbusdaemon收到客户端的消息后,查询是否存在该注册信息的回调函数,如果找不到daemon会产生错误消息,作为应答消息给客户端。找到则且执行该回调函数,将结果返回给客户端。消息发送流程:1.消息发送端申请一个GObject,绑定以下信息:Dbusname:A,Dbusobject:BDbusinterface:Csignal:D注册到dbusdaemon中2.消息接收端向dbusdaemon申请绑定注册信息为Dbusname:A,Dbusobject:BDbusinterface:CSignal为D的消息回调函数dbusdaemon收到消息发送端发出的DBUS消息后,查询是否存在该消息的绑定回调函数,且执行该回调函数。所以消息发送端是不知道谁是接收端的,这个也与DBUS底层接口实现方式不同注意:busdaemon不对消息重新排序,如果发送了两条消息到同一个进程,他们将按照发送顺序接受到。接受进程并需要按照顺序发出应答消息,例如在多线程中处理这些消息,应答消息的发出是没有顺序的。消息都有一个序列号可以与应答消息进行配对。三、通过Dbus-glib写一个服务端dbus-glib定义向dbusdaemon申请一个注册信息的形式为GObject(C语言)的对象。1.写一个XML首先,先学习怎么使用内置的xml文件自动创建出易于使用的dbus代理对象。如下的一个xml文件描述了了一个名为“HelloWorld”,输入参数为char*,输出参数为char*[]的被调用的函数。dbus的接口描述文件统一采用utf-8编码。type域数据类型定义如下:aARRAY数组bBOOLEAN布尔值dDOUBLEIEEE754双精度浮点数gSIGNATURE类型签名iINT3232位有符号整数nINT1616位有符号整数oOBJECT_PATH对象路径qUINT1616位无符号整数sSTRING零结尾的UTF-8字符串tUINT6464位无符号整数uUINT3232位无符号整数vVARIANT可以放任意数据类型的容器,数据中包含类型信息。例如glib中的GValue。xINT6464位有符号整数yBYTE8位无符号整数()定义结构时使用。例如"(i(ii))"{}定义键-值对时使用。例如"a{us}"a表示数组,数组元素的类型由a后面的标记决定。例如:"as"是字符串数组。数组"a(i(ii))"的元素是一个结构。用括号将成员的类型括起来就表示结构了,结构可以嵌套。数组"a{sv}"的元素是一个键-值对。"{sv}"表示键类型是字符串,值类型是VARIANT。一个node可以有多个interface,一个interface可以有多个method或signal,上例只以简单的单函数来说明,如果多个函数可以写成:<nodename="/"><interfacename="xxxx"><methodname="xx"><argtype="s"/><argtype="as"direction="out"/></method><methodname="xxxx"></method></interface></node>2.通过dbus-binding-tool生成头文件dbus-binding-tool目前在92服务器上就有,可以直接运行,执行指令:dbus-binding-tool--mode=glib-server--prefix=your_module_nameyour_server.xml>xxx_stub.h"--prefix"参数定义了对象前缀。设对象前缀是$(prefix),则生成的DBusGObjectInfo结构变量名就是dbus_glib_$(prefix)_object_info。绑定文件会为接口方法定义回调函数。回调函数的名称是这样的:首先将xml中的方法名称转换到全部小写,下划线分隔的格式,然后增加前缀"$(prefix)_"。例如:如果xml中有方法SendMessage,绑定文件就会引用一个名称为$(prefix)_send_message的函数。如上例中,--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定义对象typedefstructSomeObject{GObjectparent;}SomeObject;typedefstructSomeObjectClass{GObjectClassparent;}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_OBJECT3.3声明类的函数GTypesome_object_get_type(void);#defineSOME_TYPE_OBJECT(some_object_get_type())staticvoidsome_object_init(SomeObject*obj){}staticvoidsome_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_typesome_object_initsome_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生成的头文件中指定:constDBusGObjectInfodbus_glib_some_object_object_info={…}g_main_loop_new用来申请创建一个主循环,接收DBUS消息,用于服务端、消息接收端bus=dbus_g_bus_get(DBUS_BUS_SESSION,&error);if(!bus)lose_gerror("Couldn'tconnecttosessionbus",error);申请一个会话总线bus_proxy=dbus_g_proxy_new_for_name(bus,"org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus");创建连接到dbusdaemonorg.freedesktop.DBus----dbusdaemonDBUS名/org/freedesktop/DBus---dbusdaemon对象名org.freedesktop.DBus---dbusdaemoninterfaceif(!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("Failedtoacquireorg.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服务器完成,信息如下:Dbusname:org.designfu.SampleServiceDbusobject:/SomeObjectDbusinterface:test.method.TypeMethod:HelloWorld5.实现method在general_stub.h中查询method直实名称,如staticconstDBusGMethodInfodbus_glib_some_object_methods[]={{(GCallback)some_object_hello_world,dbus_glib_marshal_some_object_BOOLEAN__STRING_POINTER_POINTER,0},};即函数名为some_object_hello_world,函数的第一个输入参数固定为对象实例的指针,最后一个参数必定是GError**,中间为用户自定的参数,如本例中声明如下gbooleansome_object_hello_world(SomeObject*obj,constchar*hello_message,char***ret,GError**error)最后在代码中完成该函数实现。注意:函数声明要写在#include"general_stub.h"之前,否则编绎不识别some_object_hello_world以上这些跟对象相关的部分比较繁琐,不理解的情况下也可以把它当作公式一样记下来,只需要修改自定义的部分,其它照搬就可以了。四、通过Dbus-glib写一个客户端客户端实现较服务端简单,并不需要像DBUS底层库调用那样申请自身的DBUS名等。g_type_init();申请一个会话总线bus=dbus_g_bus_get(DBUS_BUS_SESSION,&error);if(!bus)lose_gerror("Couldn'tconnecttosessionbus",error);向dbusdeamon申请连接到以下信息的DBUS总线上remote_object=dbus_g_proxy_new_for_name(bus, "org.designfu.SampleService", "/SomeObject", "test.method.Type");调用HelloWorld程序if(!dbus_g_proxy_call(remote_object,"HelloWorld",&error, G_TYPE_STRING,"Hellofromexample-client.c!",G_TYPE_INVALID, G_TYPE_STRV,&reply_list,G_TYPE_INVALID))lose_gerror("FailedtocompleteHelloWorld",error);使用完成后释放返回参数,此例中返回值是一个字符串数组g_strfreev(reply_list);释放与目标DBUS的连接,结束g_object_unref(G_OBJECT(remote_object));五、消息发送与接收消息发送端消息发送与服务端实现基本相同,只需要在类结构构造函数中增加对信号的初始化。XML文件中增加<signalname=“xxx”/>,以下是一个同时具备接受函数调用与发送信号功能XML文件示例代码中在类结构构造函数中初始化该信号:staticvoidsome_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,而由dbusdaemon查询谁对该消息有响应,则将该消息发送给指定的进程。消息接收端消息接收端与客户端实现也基本相同,区别在于消息接收端处于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'tconnecttosessionbus",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);进入LOOPg_main_loop_run(mainloop);当收到SayHi的DBUS消息时,客户端自动执行回调函数receive_signal_handler。demo已经将函数调用与消息收发写在了一起,流程为:客户端定时2秒调用一次服务器端的函数HelloWord服务端HelloWord发送消息SayHi,附带整型数据1客户端收到消息后,消息回调函数打印出收到的数据六、效率改进以上几点大致说明了dbus-glib使用方法,但其在使用上还是有不方便的地方:1一个API要定义一个xml接口描述2数据封装非常复杂,非常不利于以后接口的扩展为了克服上面的缺点,提高可扩展性和效率,可以这样做:
如果一个应用分为client,server两端的话,要高效率的实现client/server之间
的通信,可以采用如下方式:改进一:定义一个通用的APIxml接口描述<?xmlversion="1.0"encoding="UTF-8"?><nodename="="/org/freedesktop/DBus/General_api"><interfacename="org.freedesktop.DBus.general_api"><methodname="client_request"><annotationname="org.pmonitor.DBus.GLib.request"value="_client_request_cb"/><argtype="i"name="action_id"direction="in"/> <argtype="ay"name="input_garray"direction="in"/> <argtype="ay"name="output_garray"direction="out"/><argtype="i"name="result"direction="out"/></method></interface></node>Ay表示字节的数组,其数据类型为GArray这个通用的模板关键之处就是这个Garray,Garray本身是个容器,这个容器里面可以装任何东西。我们就是利用这个GArray来实现client与server之间数据的传递,无论想传递什么要的数据。额外的参数类型支持如下:D-BustypesignatureDescriptionGTypeCtypedefFreefunctionNotesasArrayofstringsG_TYPE_STRVchar**g_strfreev
vGenericvaluecontainerG_TYPE_VALUEGValue*g_value_unsetThecallingconventionsforvaluesexpectthatmethodcallershave
allocatedreturnvalues;seebelow.数组类型集合。D-BustypesignatureDescriptionGTypeCtypedefFreefunctionNotesayArrayofbytesDBUS_TYPE_G_BYTE_ARRAYGArray*g_array_free
auArrayofuintDBUS_TYPE_G_UINT_ARRAYGArray*g_array_free
aiArrayofintDBUS_TYPE_G_INT_ARRAYGArray*g_array_free
axArrayofint64DBUS_TYPE_G_INT64_ARRAYGArray*g_array_free
atArrayofuint64DBUS_TYPE_G_UINT64_ARRAYGArray*g_array_free
adArrayofdoubleDBUS_TYPE_G_DOUBLE_ARRAYGArray*g_array_free
abArrayofbooleanDBUS_TYPE_G_BOOLEAN_ARRAYGArray*g_array_free
Hash表数组:D-BustypesignatureDescriptionGTypeCtypedefFreefunctionNotesa{ss}DictionarymappingstringstostringsDBUS_TYPE_G_STRING_STRING_HASHTABLEGHashTable*g_hash_table_destroy
改进二:用dbus的工具函数生成proxy头文件前面有说到使用dbus-binding-tool将XML脚本转换为stub.h供服务器使用dbus-binding-tool--mode=glib-server--prefix=your_module_namedbus_general.xml>general_stub.h其实将--mode值修改为glib-client,生成proxy.h文件可供客户端使用,如:
dbus-binding-tool--mode=glib-client--prefix=your_module_namedbus_general.xml>general_proxy.h打开general_proxy.h看到服务端提供的HelloWorld已经转为test_server_hello_world(DBusGProxy*proxy,constchar*IN_arg0,char***OUT_arg1,GError**error);客户端可以直接调用test_server_hello_world,而不再需要使用dbus_g_proxy_call来调用该函数。general_proxy.h中的另两个函数声明涉及异步调用,暂未使用到,没有了解phoenix中的pms与libpms就是使用统用API实现多函数调用的例子。七、注意事项1.向dbusdeamon注册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可以看到其分配的实际BusNames是不同的,比如一个是“1.5”,一个是“1.6”,因于“1.5”先创建,所以其处于dbusdeamon同名队列的顶部,所以当客户端发起DBUS通讯时,就只有“1.5”有效,当“1.5”退出后,“1.6”才会进入DBUS通讯状态。2.函数调用中的out参数,如果是服务端申请的内存空间,客户端在使用完后,要记得释放内存。如g_new-----g_free等,具体数据类型不同,其对应的申请内存与释放内存的接口不同,详细请查看/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总线时要注意使用关键字创建各自私有的总
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年神经内科各类头痛鉴别诊断模拟测验答案及解析
- 5.1 等式与方程2024-2025学年新教材七年级数学上册同步说课稿(冀教版2024)河北专版
- 2025年生理学实验生理参数监测与结果分析考察答案及解析
- 2025年口腔科牙齿修复技术考察模拟考试答案及解析
- 《第三单元 欣赏 跳圆舞曲的小猫》(教学设计)-2023-2024学年人教版(2012)音乐一年级下册
- 2025年医学信息学数据分析案例评价答案及解析
- 2025-2030中国高频直缝焊管行业发展趋势及投资风险分析报告
- 2025年肿瘤放疗护理知识检测试卷答案及解析
- 2025年内窥镜检查操作规范与器械保养答案及解析
- 2025年内科常见病例分析竞赛答案及解析
- 招录事业编人员政审表
- SG-A088接地装置安装工程工检验批质量验收记录
- 《芯片原理与技术》课件微流控芯片
- T∕ACEF 027-2021 农药污染地块土壤异味物质识别技术指南
- 建筑结构:高层建筑结构选型
- 混凝土外观质量缺陷及治理措施PPT课件
- 建设项目对海洋生物资源影响评价技术规程
- 整车轴荷计算方法
- 燃气管道焊接工艺卡
- 大陆法系的形成与发展
- 地下管线及其他地上地下设施保护加固措施(二)
评论
0/150
提交评论