再探DBUS-GLIB Binding,GLIB事件与DBUS事件是如何关联的

引用自:

http://blog.csdn.net/shallon_luo/archive/2009/04/30/4139737.aspx

 

DBus有两种API接口,一种是直接使用DBUS的 low-level API,一种是使用Binding,Bindings有不同的类型,有PERL Binding、PYTHON Binding、GLIB Binding等。这里主要关注使用GLIB binding。和low-level API不同的是,GLIB binding则能够完成GLIB OBJECT的本地事件(native signal)与DBus事件的绑定,下面描述使用DBUS,Signal事件发送和接收的基本过程。

1、Signal被发送到DBus Daemon,这个过程如果使用low-level API,这个过程需要程序直接完成;如果使用GLIB Binding,则GLIB OBJECT发送本地的Signal时自动完成。这个本地的Signal,就是g_signal_emit方法发出的GLIB OBJECT的Signal。
2、Signal包含接口的标识、Signal的标识、以及发送者的标识、其他参数。
3、DBus上的任何进程可以提交Signal的“过滤规则”。
4、DBus Daemon根据Signal过滤规则,将Signal发送到各个进程。
5、Signal接收进程接到Signal进行处理,如果使用low-level API,则直接处理Signal;如果使用的是GLIB Binding,GLIB Binding将在其代理对象上触发一个本地事件(emit a native signal )


下面回到DBus-GLIB的例子程序,详细说明这个过程。
DBus-GLIB Binding的例子运行过程如《初探DBUS(1)》一文所示(如果有疑问需要获取源码请参见 初探DBUS(1) ),在例子主干逻辑如下:
1、example-signal-recipient程序负责定期向发起
example-signal-emitter程序emitHelloSignal 远程方法调用
2、example-signal-emitter程序为emitHelloSignal的服务器端,接收到调用后,向 example-signal-recipient发送HELLO_SIGNAL的SIGNAL事件
3、
example-signal-recipient接收到事件并打印。

example-signal-emitter程序的分析如下:
(1)从example-signal-emitter.xml文件生成对应的
example-signal-emitter- glue.h文件

example-signal-emitter.xml文件如下:


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

<node name="/">
  <interface name="org.designfu.TestService">

    <method name="emitHelloSignal">
    </method>
    
    <!-- Mark the signal as exported -->
    <signal name="HelloSignal"/>

  </interface>
</node>

命令行如下:
dbus-binding-tool --prefix=test_object --mode=glib-server --output=example-signal-emitter-glue.h ./example-signal-emitter.xml

example-signal-emitter-
glue.h定义了本地的对象中哪个Signal与DBus的Signal绑定。 example-signal-emitter- glue.h 原码中定义了HelloSignal的DBus 的Signal与本地定义Signal绑定
const DBusGObjectInfo dbus_glib_test_object_object_info = {
  0,
  dbus_glib_test_object_methods,
  1,
"org.designfu.TestService\0emitHelloSignal\0S\0\0\0",
"org.designfu.TestService\0HelloSignal\0\0",
"\0"
};

(2)example-signal-emitter.c中定义了一个GOBJECT风格的“对象” TestObject。TestObject的class_init方法定义了hello_signal 的本地Signal。关于 GOBJECT 的对象模型请查看GOBJECT的相关文档。
static void test_object_class_init (TestObjectClass *klass)
{
 signals[HELLO_SIGNAL] =
    g_signal_new ("hello_signal",
          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_STRING);
}

这里有一个问题,名为“HelloSignal”的DBus事件如何同 名为“hello_signal”关联的?这个问题可以从./dbus-gobject.c的export_signals 函数得到解答,该函数调用了s = _dbus_gutils_wincaps_to_uscore (signame); _dbus_gutils_wincaps_to_uscore 这个函数将 HelloSignal翻译成了hello_signal

(3) 当example-signal-emitter在接收到emitHelloSignal 远程方法调用中 发出"hello_signal"的本地Signal
g_signal_emit (obj, signals[HELLO_SIGNAL], 0, "Hello");

本地的Signal如何触发DBus的Signal呢?
到回example-signal-emitter的主函数继续分析
 
(3.1)初始化

  DBusGConnection *bus;
  DBusGProxy *bus_proxy;
  GError *error = NULL;
  TestObject *obj;
  GMainLoop *mainloop;
  guint request_name_result;

  g_type_init ();

  dbus_g_object_type_install_info (TEST_TYPE_OBJECT, &dbus_glib_test_object_object_info);


dbus_glib_test_object_object_info见第(1)步的的说明

(3.2)设置本地Signal与DBus Signal的绑定

  mainloop = g_main_loop_new (NULL, FALSE);

  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");

  if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error,
              G_TYPE_STRING, "org.designfu.TestService",
              G_TYPE_UINT, 0,
              G_TYPE_INVALID,
              G_TYPE_UINT, &request_name_result,
              G_TYPE_INVALID))
    lose_gerror ("Failed to acquire org.designfu.TestService", error);

  obj = g_object_new (TEST_TYPE_OBJECT, NULL);

 
  dbus_g_connection_register_g_object (bus, "/org/designfu/TestService/object", G_OBJECT (obj));

  printf ("test service running\n");

  g_main_loop_run (mainloop);

  exit (0);


dbus_g_connection_register_g_object 方法中调用了上文提到的export_signals , export_signals 对每个export的本地signal进行了如下的操作:

          closure = dbus_g_signal_closure_new (connection, object, signame, (char*) iface);
          g_closure_set_marshal (closure, signal_emitter_marshaller);

          g_signal_connect_closure_by_id (object,
                          id,
                          0,
                          closure,
                          FALSE);

          g_closure_add_finalize_notifier (closure, NULL,
                           dbus_g_signal_closure_finalize);


closure是什么呢?文档说“A GClosure represents a callback supplied by the programmer.” g_signal_connect_closure_by_id函数为每个本地的Signal(参数id,是本地Signal的ID)挂载一个处理回调的 closure, closure中关键函数是 signal_emitter_marshaller

static void
signal_emitter_marshaller (GClosure        *closure,
                           GValue          *retval,
                           guint            n_param_values,
                           const GValue    *param_values,
                           gpointer         invocation_hint,
                           gpointer         marshal_data)
{
  DBusGSignalClosure *sigclosure;
  DBusMessage *signal;
  DBusMessageIter iter;
  guint i;
  const char *path;

  sigclosure = (DBusGSignalClosure *) closure;

  g_assert (retval == NULL);

  path = _dbus_gobject_get_path (sigclosure->object);

  g_assert (path != NULL);

  signal = dbus_message_new_signal (path,
                                    sigclosure->sigiface,
                                    sigclosure->signame);
  if (!signal)
    {
      g_error ("out of memory");
      return;
    }

  dbus_message_iter_init_append (signal, &iter);

  /* First argument is the object itself, and we can't marshall that */
  for (i = 1; i < n_param_values; i++)
    {
      if (!_dbus_gvalue_marshal (&iter,
                                (GValue *) (&(param_values[i]))))
        {
          g_warning ("failed to marshal parameter %d for signal %s",
                     i, sigclosure->signame);
          goto out;
        }
    }
  dbus_connection_send (DBUS_CONNECTION_FROM_G_CONNECTION (sigclosure->connection),
                        signal, NULL);
 out:
  dbus_message_unref (signal);
}

可 以很清晰的看到,dbus_connection_send 的过程,于是,本地的signal触发--〉dbus_g_closure-->signal_emitter_marshaller,在 signal_emitter_marshaller将DBus的Signal发送了出去。



-----------------------------------------------------------------------------------------------------------------
example-signal-recipient的主干逻辑代码如下:

(1)初始化Session Bus连接,并获取远程对象。
  DBusGConnection *bus;
  DBusGProxy *remote_object;
  GError *error = NULL;
  GMainLoop *mainloop;

  g_type_init ();

  mainloop = g_main_loop_new (NULL, FALSE);

  bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
  if (!bus)
    lose_gerror ("Couldn't connect to session bus", error);
 
  /* We use _for_name_owner in order to track this particular service
   * instance, which lets us receive signals.
   */
  remote_object = dbus_g_proxy_new_for_name (bus,
                         "org.designfu.TestService",
                         "/org/designfu/TestService/object",
                         "org.designfu.TestService");
  if (!remote_object)
    lose_gerror ("Failed to get name owner", error);


(2)被注释掉的重要步骤:注册Signal处理的marshaller。
由 于例子使用的远程调用方法emitHelloSignal不带参数,因此可以使用系统内置的marshaller,因此例子程序中将 dbus_g_object_register_marshaller注释了。这里参照pidgin 项目(另一个使用DBus的著名开源项目)的DBus用法,将该步骤补上,在pidgin的例子中,Signal的interface名 为"im.pidgin.purple.PurpleInterface"
(2.1)查看Signal的“函数原型”,使用dbus-monitor命令进行查找:
命令行:dbus-monitor type=signal interface="im.pidgin.purple.PurpleInterface"
结果:
signal sender=:1.21 -> dest=(null destination) path=/im/pidgin/purple/PurpleObject; interface=im.pidgin.purple.PurpleInterface; member=ReceivedImMsg
 int32 1097
 string "mybuddy@hotmail.com"
 string "<FONT FACE="Times"><FONT COLOR="#000000">Hi!</FONT></FONT>"
 int32 8728
 uint32 0
(2.2)生成



marshal.list文件



内容如下:

VOID:INT,STRING,STRING,INT,UINT

其含义是:返回为VOID,参数类型按次序为
INT,STRING,STRING,INT,UINT


(2.3)生成marshal.h和marshal.c

glib-genmarshal --header --prefix=marshal marshal.list > marshal.h
glib-genmarshal --body --prefix=marshal marshal.list > marshal.c

(2.4)注册marshaller




 

 dbus_g_object_register_marshaller(marshal_VOID__INT_STRING_STRING_INT_UINT, 
 G_TYPE_NONE, G_TYPE_INT, G_TYPE_STRING, 
 G_TYPE_STRING, G_TYPE_INT, G_TYPE_UINT, 
 G_TYPE_INVALID);



(3)设置需处理的HelloSignal事件

dbus_g_proxy_add_signal (remote_object, "HelloSignal", G_TYPE_STRING, G_TYPE_INVALID);


dbus_g_proxy_connect_signal (remote_object, "HelloSignal", G_CALLBACK (hello_signal_handler),
                   NULL, NULL);
 


(4)设置一个定时器,定期发起
emitHelloSignal调用。
定时器回调方法如下:

static gboolean emit_signal (gpointer arg)
{
  DBusGProxy *proxy = arg;
 
  dbus_g_proxy_call_no_reply (proxy, "emitHelloSignal", G_TYPE_INVALID);
  return TRUE;
}

 

========================================================

初探dbus(1)

本文记录了对dbus的初次探索,只是工作过程,如果对dbus不了解,则可以先看看

http://blog.csdn.net/fmddlmyy/archive/2008/12/23/3585730.aspx

1、以shallon用户登录ubuntu

2、su - 获得Root的权限

3、获取Dbus的原码,使用DBUS有几种方式,
(1)原始的C level的API,为了兼容旧有程序而存在,不推荐使用
(2)GLIB的Binding 推荐使用,GLIB是一种以C语言得语法模拟C++的开发类库,我们将选用它。
(3)Python的及其他语言的Binding


4、获取Dbus-GLIB的原码,其运行例子代码下边要用到

git clone git://anongit.freedesktop.org/git/dbus/dbus-glib


5、编译Dbus-GLIB中的example目录,不用编译DBUS和DBUS-GLIB,因为我的机器ubuntu上已经安装好,并缺省就运行了DBUS的进程。

5.1编译脚本自动使用dbus-binding-tool 生成接口描述文件对应的头文件

dbus-binding-tool --prefix=some_object --mode=glib-server --output=example-service-glue.h ./example-service.xml
dbus-binding-tool --prefix=test_object --mode=glib-server --output=example-signal-emitter-glue.h ./example-signal-emitter.xml


6运行example-signal-recipient向Session bus发送消息,下面如实的记录了排错的过程

6.1./example-signal-recipient
Couldn't connect to session bus: dbus-launch failed to autolaunch D-Bus session: Autolaunch error: X11 initialization failed.

6.2看样子Dbus依赖X11的环境,于是设置环境变量,再运行
export DISPLAY=:0.0

 ./example-signal-recipient
Couldn't connect to session bus: dbus-launch failed to autolaunch D-Bus session: No protocol specified
Autolaunch error: X11 initialization failed.

6.3查看DBUS的进程启动的owner,Session Bus为本地用户

108       4685     1  0 08:31 ?        00:00:13 /bin/dbus-daemon --system
shallon   5652     1  0 08:36 ?        00:00:00 /usr/bin/dbus-launch --exit-with-session /usr/bin/pulse-session /usr/bin/seahorse-agent --execute x-session-manager
shallon   5655     1  0 08:36 ?        00:00:00 //bin/dbus-daemon --fork --print-pid 6 --print-address 9 --session
 
6.4怀疑是环境问题,退出 su -的环境,使用su root进去再运行

 ./example-signal-recipient
Couldn't connect to session bus: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.


6.5查询网上资料,发现Session bus需要访问启动用户所在home目录的隐藏文件夹/home/shallon/.dbus/,该目录的所有者为shallon,于是退出su 使用shallon用户的环境运行

6.6成功运行
第一个终端:
shallon@shallon-laptop:~/open_source/dbus-glib/dbus/examples$ ./example-signal-recipient
Received signal and it says: Hello
Received signal and it says: Hello
第二个终端:
shallon@shallon-laptop:~/open_source/dbus-glib/dbus/examples$ ./example-signal-emitter
test service running
第三个终端使用 dbus-monitor看到

method call sender=:1.86 -> dest=org.designfu.TestService path=/org/designfu/TestService/object; interface=org.designfu.TestService; member=emitHelloSignal
signal sender=:1.87 -> dest=(null destination) path=/org/designfu/TestService/object; interface=org.designfu.TestService; member=HelloSignal
   string "Hello"
method call sender=:1.86 -> dest=org.designfu.TestService path=/org/designfu/TestService/object; interface=org.designfu.TestService; member=emitHelloSignal
signal sender=:1.87 -> dest=(null destination) path=/org/designfu/TestService/object; interface=org.designfu.TestService; member=HelloSignal
   string "Hello"

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值