zoukankan      html  css  js  c++  java
  • [转]DBUSGLIB Binding,GLIB事件与DBUS事件是如何关联的

    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;

    }

    原文地址:http://blog.csdn.net/shallon_luo/archive/2009/05/05/4150937.aspx

  • 相关阅读:
    vim的script、function及command
    Vim中如何移动光标
    command模式下命令的匹配及help内容的搜索
    为什么vim编辑模式下ctrl-w可以前向删除单词及按键映射的展开
    gcc如何实现C++中函数auto返回类型推导
    sqlserver 列转行
    【java笔记】可变长参数(...)
    【c#笔记】可变长参数(params)
    【c#笔记】c#与java的差异:接口定义实现
    【java笔记】Calendar类的陷阱
  • 原文地址:https://www.cnblogs.com/juncent/p/1858436.html
Copyright © 2011-2022 走看看