首页
新闻
论坛
群组
Blog
文档
下载
读书
Tag
网摘
搜索
.NET
Java
游戏
视频
人才
外包
培训
数据库
书店
程序员
欢迎您:
游客
| 退出
| 登录
注册
帮助
我的帖子
我参与的帖子
我的空间
我的网摘
CSDN
CSDN社区
Web 开发
ColdFusion
将帖子提前
放进我的网摘
推荐给好友
我要提问
帖子加分
生成帖子
置顶
推荐(加精)
取消推荐(加精)
锁定帖子
移动帖子
取消引用
结贴去...
管理菜单
页面风格切换
标准风格
老版本论坛
DBus使用非缺省Glib的maincontext的问题
加为好友
发送私信
在线聊天
yuhang111
宇航
等级:
可用分等级:
富农
总技术专家分:
5
总技术专家分排名:
246308
揭帖率:
81.82%
发表于:
2007-08-27 09:11:16
楼主
转:http://blog.csdn.net/flyfish30/archive/2007/01/21/1489531.aspx
MMI中间层封装库已经移植到linux平台下稳定运行一两个月了,最近在给其加入插件系统时,却遇到了一点麻烦。插件中的数据库数据变化的Glib信号处理函数在其他应用对数据库进行插入、修改等操作时不会被调用,即无法接收到数据变化的Glib信号。
我们的数据库封装采用C/S架构,客户端使用DBus和服务端通讯,当数据库的数据发生变化时(插入、修改、删除)时,服务端通过DBus向客户端发送相应的Glib信号,通知客户端数据发生变化。MMI中间层也是采用C/S架构,也使用DBus实现客户端和服务端的通讯,如接收应用的请求,返回处理结果,主动上报GSM模组的事件。MMI是一个多线程的服务进程,其主线程获取了一个DBus连接,用来和客户端的通讯。在我们的设计中,MMI还要操作数据库,监控数据库的数据变化,如接收到新短信后,需要合并长短信,将短信存储到数据库中。这些和数据库的交互我们将其交给插件去实现,简化了MMI中间层的实现,和数据库解耦,并使得MMI中间层可以较容易的移植到Nucleus等嵌入式实时操作系统上,这些系统一般没有数据库。MMI中间层的插件加载后,通过数据库封装的持久化对象也获取了一个DBus连接,用来和数据库服务端通讯。现在的问题是,插件获取的 DBus连接不能接收到数据库服务端发出的数据变化的Glib信号,通过调试信息的输出,发现数据库服务端在数据变化时已确实发出了Glib信号。因此,问题应该出在DBus的信号发送过程上。
年初的时候花了一段时间看DBus的代码,对DBus的机制和实现有了一定的了解。为了简化问题的查找和重现,我使用DBus的Glib封装自带的信号发送和接收的例子,将其修改为多线程程序,模拟MMI插件方式使用DBus,编译、运行。从问题的现象看,感觉是我们获得的DBus连接是共享的导致的问题,共享的连接使用同一个消息派发函数和DBus的watch,可能是这个导致Glib信号不能接收。于是子线程改为获取私有的连接,但还是不行。于是重新检查代码,觉得应该和子线程的DBus没有绑定到缺省的GThread的maincontext有关,因为之前我们发现子线程若使用缺省的maincontext在和主线程用管道等进行通讯时会有问题,因此子线程使用非缺省的maincontext。为了将DBus绑定到非缺省的maincontext,在调用函数dbus_g_bus_get()后,调用函数dbus_connection_setup_with_g_main(),而DBus绑定到缺省的maincontext时,不需要调用后面这个函数,此时是能接收到Glib信号的,看来是调用了函数dbus_connection_setup_with_g_main()引起问题的。在函数dbus_g_bus_get()中,为了将获取的DBus连接绑定到缺省的maincontext,已经调用了一次函数dbus_connection_setup_with_g_main(),传入的maincontext为NULL。当我们要将DBus连接绑定到非缺省的maincontext时,再次调用了函数dbus_connection_setup_with_g_main(),将DBus连接绑定到传入的maincontext上,似乎这次绑定没有成功。但DBus作为一个使用已较为广泛的软件,而且为了查找问题,我使用了DBus的1.0.2版本,Glib封装的0.72版本,应该不会出现API函数调用失效的问题吧,是否我们哪里使用不当呢。让我们深入函数dbus_connection_setup_with_g_main(),看看DBus是如何绑定到maincontext上的,如何接收Glib信号,并调用Glib信号处理函数的。
在深入分析之前,我们先看看DBus的是如何与Glib的mainloop关联的,如何监控传输端口的。DBus是一个按面向对象设计和实现的软件,因此我将这些C结构称之为对象。描述DBus连接的对象类型是DBusConnection,其Glib封装的对象类型是DBusGConnection。该对象中有一个名为watches的DBusWatchList类型的链表,这是由所有传输端口监控器(按其英文原意我称之为看守者对象)组成的链表,还有一个Glib封装使用的对象数据ConnectionSetup,通过函数dbus_connection_get_data()和dbus_connection_set_data()来获取和设置。
看守者对象(DBusWatch)监控传输端口,该传输端口的文件描述符由成员fd保存,看守对象的处理函数及处理函数的用户数据由函数指针成员handler和数据指针handler_data保存。看守者对象如何与Glib的mainloop关联呢,关键就在其对象数据data中,data指向一个IOHandler类型的对象,IOHandler对象类型中有一个成员source指向GSource类型的对象。熟悉Glib的朋友都知道GSource是Glib的mainloop的源,mainloop在运行时会遍历所有绑定在其上的源,当某个源可用时(对传输端口而言是数据已发送或接收到数据),会调用这个源的回调函数,在IOHandler类型的对象中,这个回调函数就是io_handler_dispatch()。这个函数调用DBusWatch类型的函数dbus_watch_handler(),最终调用看守对象的处理函数,实现传输端口数据发送和接收的处理,在我们的这个问题中,则是进行数据接收的处理。当服务端的Glib对象发送出一个导出到客户端的Glib的信号时,DBus将该信号打包为一个DBus的signal类型的消息,通过传输端口经DBus后台进程转发传送到客户端。当客户端接收到这个signal类型的消息时,通过上述的mainloop机制,看守者对象的处理函数将被调用,该处理函数将signal类型的消息解包并将其转化为Glib信号发送出去,应用相应的Glib信号处理函数就会调用。
链表watches中有几个函数指针add_watch_function,remove_watch_function,watch_toggled_function,分别指向Glib封装实现的对应函数,当向watches增加watch、从watches移除watch、触发watches中的watch时,会调用相应的函数指针指向的函数,完成Glib封装附加的处理。当调用函数dbus_connection_setup_with_g_main(),将DBus连接绑定到指定的maincontext时,需要调用函数dbus_connection_set_watch_function(),将Glib封装实现的add_watch、remove_watch、watch_toggled这几个函数设置到watches的函数指针中。Glib封装实现的add_watch函数将看守者绑定到指定的maincontext中,该函数创建了一个IOHandler类型的对象,并将该对象指向的GSource类型的对象绑定到DBus连接要绑定到的maincontext中。Glib封装实现的remove_watch函数将看守者从指定的maincontext中脱离,该函数将IOHandler指向的GSource类型的对象从maincontext的绑定脱离,并销毁这个GSource类型的对象。Glib封装实现的watch_toggled函数与我们的问题无关,就不介绍了。函数dbus_connection_set_watch_function()除了将Glib封装实现的函数设置到watches的函数指针中,还对watches中的看守者逐个调用add_watch函数将其绑定到指定的maincontext中,逐个调用remove_watch函数将其从指定的maincontext中脱离。
了解了DBus和Glib的mainloop的绑定机制,我们就可以来分析我们的问题了。前面的初步分析已经指出在再次调用函数dbus_connection_setup_with_g_main()将DBus连接绑定到指定的maincontext上后,客户端不能接收Glib信号。从上面的绑定机制来看,则是重新设置了add_watch、remove_watch、watch_toggled这几个函数,并对watches的每个看守者对象多调用了一次函数add_watch和remove_watch。给函数指针设置新的函数是不会有问题的,问题应该是多一次的add_watch和remove_watch函数调用引起的。于是在函数add_watch和remove_watch中加入调试信息的打印。在函数add_watch中,创建IOHandler类型的对象,将该对象的source对象绑定到maincontext时,打印出该source对象的内存地址。在函数remove_watch中,将IOHandler中的source对象从maincontext脱离并销毁时,打印出该source对象的内存地址。再次运行程序后,发现第二此调用函数dbus_connection_setup_with_g_main()时,函数remove_watch销毁的source对象和函数add_watch创建的IOHandler中的source对象是同一个对象。因函数remove_watch在函数add_watch之后调用,最终这个source对象是被销毁了,不能被mainloop遍历,也就无法监控传输端口的数据了,导致客户端不能接收服务端发送出的Glib信号。这就是问题的根本原因。
问题的原因找到了,但是却很不好解决,主要是看守者对象只有一个对象数据data用来保存IOHandler类型的对象,也就只有一个source对象和其关联。当add_watch函数将创建的IOHandler类型的对象保存到data中后,为了将看守者对象从原先的maincontext脱离,对该看守者调用remove_watch函数时将和其关联的source对象销毁时,销毁的是新创建的source对象,无法区别对待。为解决这个问题,我们只好重新写了一个获取DBus连接的函数dbus_g_bus_get_private()以获取私有的DBus连接,并将该连接绑定到指定的非缺省的maincontext中。这个函数只调用一次dbus_connection_setup_with_g_main()函数,避免出现看守者关联的source对象被销毁的问题。不知各位有否其他解决方法,请不吝赐教。
问题点数:
0
回复次数:
3
显示所有回复
显示星级回复
显示楼主回复
修改
删除
举报
引用
回复
加为好友
发送私信
在线聊天
knowledge_Is_Life
阿凡
等级:
可用分等级:
短工
总技术专家分:
9295
总技术专家分排名:
2117
发表于:
2008-04-30 10:14:42
1
楼 得分:
0
该回复于2008-05-01 06:26:25被版主删除
修改
删除
举报
引用
回复
加为好友
发送私信
在线聊天
UltraBejing
OneNightInBejing
等级:
可用分等级:
短工
总技术专家分:
8607
总技术专家分排名:
2313
发表于:
2008-05-01 01:30:29
2
楼 得分:
0
该回复于2008-05-01 06:29:54被版主删除
修改
删除
举报
引用
回复
加为好友
发送私信
在线聊天
marka945
163.com
等级:
可用分等级:
短工
总技术专家分:
161
总技术专家分排名:
67737
发表于:
2008-05-20 17:11:08
3
楼 得分:
0
楼主我认识你.
修改
删除
举报
引用
回复
将帖子提前
放进我的网摘
推荐给好友
我要提问
帖子加分
结贴去...
管理菜单
页面风格切换
标准风格
老版本论坛
网站简介
-
广告服务
-
网站地图
-
帮助
-
联系方式
-
诚聘英才
-
English
-
问题报告
北京创新乐知广告有限公司 版权所有 京 ICP 证 070598 号
世纪乐知(北京)网络技术有限公司 提供技术支持
Copyright © 2000-2008, CSDN.NET, All Rights Reserved
abc推荐给好友