linux 进程间通信 dbus-glib【实例】详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)
linux 進程間通信 dbus-glib【實例】詳解一(附代碼)(d-feet工具使用)
linux 進程間通信 dbus-glib【實例】詳解二(上) 消息和消息總線(附代碼)
linux 進程間通信 dbus-glib【實例】詳解二(下) 消息和消息總線(ListActivatableNames和服務器的自動啟動)(附代碼)
linux 進程間通信 dbus-glib【實例】詳解三(下) 數據類型和dteeth(類型簽名type域)(層級結構:服務Service --> Node(對象、object) 等 )(附代碼)
linux 進程間通信 dbus-glib【實例】詳解四(上) C庫 dbus-glib 使用(附代碼)(編寫接口描述文件.xml,dbus-binding-tool工具生成綁定文件)
文章目錄
- dbus實例講解(四上):使用dbus-glib
- 1、接口
- 1.1、編寫接口描述文件
- 1.2、由接口描述文件生成綁定文件
- 2 對象
- 2.1 對象定義
- 2.2 信號的列集函數
- 2.3 對象實現(xiàn)
- 3 主程序
- 3.1 登記dbus服務器
- 3.2 IO Channel
- 3.3 編譯運行
dbus實例講解(四上):使用dbus-glib
dbus-glib是dbus底層接口的一個封裝。本講我們用dbus-glib做一個dus接口,并寫一個客戶程序。
1、接口
1.1、編寫接口描述文件
首先編寫接口描述文件。我們要實現(xiàn)的連接的公共名是"org.freesmartphone.ogsmd",接口描述文件如下:
$ cat smss.xml <?xml version="1.0" encoding="UTF-8" ?><node name="/org/freesmartphone/GSM/Device"><interface name="org.freesmartphone.GSM.SMS"><method name="SendMessage"><arg name="number" type="s"/><arg name="contents" type="s"/><arg name="featuremap" type="a{sv}"/><arg type="i" direction="out"/></method><signal name="IncomingMessage"><arg name="address" type="s"/><arg name="contents" type="s"/><arg name="features" type="a{sv}"/></signal></interface> </node>我們要在連接"org.freesmartphone.ogsmd"中的實現(xiàn)對象"/org/freesmartphone/GSM/Device"。 這個對象有接口"org.freesmartphone.GSM.SMS"。這個接口有一個SendMessage方法和一個IncomingMessage信號。
SendMessage方法和IncomingMessage信號除了兩個字符串參數外,還有一個a{sv}參數,這是一個哈希表,即python的字典。 鍵-值對的鍵類型是字符串,值類型是VARIANT。這個接口是openmoko fso接口的一部分。 但為簡單起見,本例在哈希表部分,只用三個鍵值。
鍵"alphabet"對應的值類型是字符串。
鍵"csm_num"對應的值類型是INT32。
鍵"csm_seq"對應的值類型是INT32。
請注意方法和信號名應采用單詞連寫,首字母大寫的格式。
1.2、由接口描述文件生成綁定文件
有一個叫dbus-binding-tool的工具,它讀入接口描述文件,產生一個綁定文件。這個文件包含了dbus對象的接口信息。 在主程序中我們通過dbus_g_object_type_install_info函數向dbus-glib登記對象信息(DBusGObjectInfo結構)。
本例使用了autotool,在Makefile.am中可以這樣調用dbus-binding-tool:
smss-glue.h: smss.xml$(LIBTOOL) --mode=execute dbus-binding-tool --prefix=gsm_sms --mode=glib-server --output=smss-glue.h $(srcdir)/smss.xml
"--prefix"參數定義了對象前綴。設對象前綴是$(prefix),則生成的DBusGObjectInfo結構變量名就是dbus_glib_$(prefix)_object_info。 綁定文件會為接口方法定義回調函數。回調函數的名稱是這樣的:首先將xml中的方法名稱轉換到全部小寫,下劃線分隔的格式,然后增加前綴"$(prefix)_"。 例如:如果xml中有方法SendMessage,綁定文件就會引用一個名稱為$(prefix)_send_message的函數。
綁定文件還會為接口方法生成用于散集(Unmarshaling)(反序列化?)的函數。在dbus消息中,方法參數是以流格式存在的。 該函數將方法參數由數據流還原到glib的數據格式,并傳入方法的回調函數。 本例中,dbus-binding-tool生成以下的smss-glue.h:
Dontla:先照著前面創(chuàng)建smss.xml文件,然后我在ubuntu上終端執(zhí)行:dbus-binding-tool --mode=glib-server --prefix=gsm_sms smss.xml>smss-glue.h
參照:https://dontla.blog.csdn.net/article/details/122531787
(smss-glue.h)
在包含綁定文件前,我們必須聲明綁定文件要引用的回調函數。(什么意思?)
2 對象
2.1 對象定義
dbus-glib用GObject實現(xiàn)dbus對象。所以我們首先要實現(xiàn)一個對象。在本例中,我們實現(xiàn)一個GsmSms對象,它繼承了GObject:
(gsm_sms.h)
$ cat gsm_sms.h #ifndef GSM_SMS_H #define GSM_SMS_Htypedef struct GsmSms GsmSms; typedef struct GsmSmsClass GsmSmsClass;struct GsmSms {GObject parent; };struct GsmSmsClass {GObjectClass parent; };#define GSM_SMS_TYPE (gsm_sms_get_type ())GType gsm_sms_get_type (void); gboolean gsm_sms_send_message (GsmSms *obj, const char *number, const char *contents, GHashTable *featuremap, int *ret, GError **error); void gsm_sms_emit_incoming_message(GsmSms *obj, const char * address, const char * contents, GHashTable *hash);#endifGObject的對象定義雖然繁瑣,但有固定的套路。依樣畫葫蘆,畫多了就習慣了。我們在gsm_sms.h中聲明了gsm_sms_send_message函數。 gsm_sms_send_message函數是在gsm_sms.c中實現(xiàn)的,在綁定文件smss-glue.h中用到。 因為主程序要使用綁定文件中的對象信息,所以應由主程序包含綁定文件。 主程序只要在包含綁定文件前包含gsm_sms.h,編譯器就不會抱怨gsm_sms_send_message函數未聲明。
2.2 信號的列集函數
列集(Marshaling)是將數據從某種格式存為流格式的操作;散集(Unmarshaling)則是列集的反操作,將信息從流格式中還原出來。 在綁定文件中,dbus-binding-tool自動生成函數將方法參數從dbus消息中還原出來,即實現(xiàn)了散集。 那么我們怎么把信號參數由glib的數據結構轉換到消息中的數據流呢?
因為GsmSms對象有一個信號,所以在對象類初始化函數gsm_sms_class_init中,我們要調用g_signal_new創(chuàng)建信號。 g_signal_new要求我們提供一個列集函數。
glib有一些標準的列集函數,在gmarshal.h中定義。例如g_cclosure_marshal_VOID__STRING,這個函數適合只有一個字符串參數的信號。 如果gmarshal.h沒有提供適合的列集函數,我們可以用一個叫glib-genmarshal的工具自動生成列集函數。 后面我們會看到,無論是標準列集函數還是生成的列集函數都是既可以用于列集也可以用于散集,這些函數通常都被稱作列集函數。
使用glib-genmarshal前,我們同樣要準備一個輸入文件:
$ cat sms-marshal.list # see glib-genmarshal(1) for a detailed description of the file format, # possible parameter types are: # VOID indicates no return type, or no extra # parameters. if VOID is used as the parameter # list, no additional parameters may be present. # BOOLEAN for boolean types (gboolean) # CHAR for signed char types (gchar) # UCHAR for unsigned char types (guchar) # INT for signed integer types (gint) # UINT for unsigned integer types (guint) # LONG for signed long integer types (glong) # ULONG for unsigned long integer types (gulong) # ENUM for enumeration types (gint) # FLAGS for flag enumeration types (guint) # FLOAT for single-precision float types (gfloat) # DOUBLE for double-precision float types (gdouble) # STRING for string types (gchar*) # PARAM for GParamSpec or derived types (GParamSpec*) # BOXED for boxed (anonymous but reference counted) types (GBoxed*) # POINTER for anonymous pointer types (gpointer) # OBJECT for GObject or derived types (GObject*) # NONE deprecated alias for VOID # BOOL deprecated alias for BOOLEAN VOID:STRING,STRING,BOXED我們需要的函數返回類型是VOID,參數是STRING,STRING,BOXED。在Makefile.am中可以這樣調用glib-genmarshal:
sms-marshal.h: sms-marshal.list$(LIBTOOL) --mode=execute glib-genmarshal --header sms-marshal.list --prefix=sms_marshal > sms-marshal.hsms-marshal.c: sms-marshal.list$(LIBTOOL) --mode=execute glib-genmarshal --body sms-marshal.list --prefix=sms_marshal > sms-marshal.c"--prefix"和函數原型決定輸出函數名。如果"--prefix=sms_marshal",函數原型是"OID:STRING,STRING,BOXED", 生成的列集函數名就必然是sms_marshal_VOID__STRING_STRING_BOXED。
在本例中自動生成的文件內容如下:
$ cat sms-marshal.h#ifndef __sms_marshal_MARSHAL_H__ #define __sms_marshal_MARSHAL_H__#include <glib-object.h>G_BEGIN_DECLS/* VOID:STRING,STRING,BOXED (sms-marshal.list:24) */ extern void sms_marshal_VOID__STRING_STRING_BOXED (GClosure *closure,GValue *return_value,guint n_param_values,const GValue *param_values,gpointer invocation_hint,gpointer marshal_data);G_END_DECLS#endif /* __sms_marshal_MARSHAL_H__ */$ cat sms-marshal.c#include <glib-object.h>#ifdef G_ENABLE_DEBUG #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) #define g_marshal_value_peek_char(v) g_value_get_char (v) #define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) #define g_marshal_value_peek_int(v) g_value_get_int (v) #define g_marshal_value_peek_uint(v) g_value_get_uint (v) #define g_marshal_value_peek_long(v) g_value_get_long (v) #define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) #define g_marshal_value_peek_int64(v) g_value_get_int64 (v) #define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) #define g_marshal_value_peek_enum(v) g_value_get_enum (v) #define g_marshal_value_peek_flags(v) g_value_get_flags (v) #define g_marshal_value_peek_float(v) g_value_get_float (v) #define g_marshal_value_peek_double(v) g_value_get_double (v) #define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) #define g_marshal_value_peek_param(v) g_value_get_param (v) #define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) #define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) #define g_marshal_value_peek_object(v) g_value_get_object (v) #else /* !G_ENABLE_DEBUG */ /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.* Do not access GValues directly in your code. Instead, use the* g_value_get_*() functions*/ #define g_marshal_value_peek_boolean(v) (v)->data[0].v_int #define g_marshal_value_peek_char(v) (v)->data[0].v_int #define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint #define g_marshal_value_peek_int(v) (v)->data[0].v_int #define g_marshal_value_peek_uint(v) (v)->data[0].v_uint #define g_marshal_value_peek_long(v) (v)->data[0].v_long #define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong #define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 #define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 #define g_marshal_value_peek_enum(v) (v)->data[0].v_long #define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong #define g_marshal_value_peek_float(v) (v)->data[0].v_float #define g_marshal_value_peek_double(v) (v)->data[0].v_double #define g_marshal_value_peek_string(v) (v)->data[0].v_pointer #define g_marshal_value_peek_param(v) (v)->data[0].v_pointer #define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer #define g_marshal_value_peek_object(v) (v)->data[0].v_pointer #endif /* !G_ENABLE_DEBUG *//* VOID:STRING,STRING,BOXED (sms-marshal.list:24) */ void sms_marshal_VOID__STRING_STRING_BOXED (GClosure *closure,GValue *return_value,guint n_param_values,const GValue *param_values,gpointer invocation_hint,gpointer marshal_data) {typedef void (*GMarshalFunc_VOID__STRING_STRING_BOXED) (gpointer data1,gpointer arg_1,gpointer arg_2,gpointer arg_3,gpointer data2);register GMarshalFunc_VOID__STRING_STRING_BOXED callback;register GCClosure *cc = (GCClosure*) closure;register gpointer data1, data2;g_return_if_fail (n_param_values == 4);if (G_CCLOSURE_SWAP_DATA (closure)){data1 = closure->data;data2 = g_value_peek_pointer (param_values + 0);}else{data1 = g_value_peek_pointer (param_values + 0);data2 = closure->data;}callback = (GMarshalFunc_VOID__STRING_STRING_BOXED) (marshal_data ? marshal_data : cc->callback);callback (data1,g_marshal_value_peek_string (param_values + 1),g_marshal_value_peek_string (param_values + 2),g_marshal_value_peek_boxed (param_values + 3),data2); }2.3 對象實現(xiàn)
準備好列集函數后,我們來實現(xiàn)GsmSms。
$ cat -n gsm_sms.c1 #include <dbus/dbus-glib.h>2 #include <stdio.h>3 #include <stdlib.h>4 #include <string.h>5 #include "gsm_sms.h"6 #include "sms-marshal.h"7 #include "sms_features.h"89 enum10 {11 INCOMING_MESSAGE,12 LAST_SIGNAL13 };1415 static guint signals[LAST_SIGNAL];1617 G_DEFINE_TYPE(GsmSms, gsm_sms, G_TYPE_OBJECT)1819 static void gsm_sms_init (GsmSms *obj)20 {21 }2223 static void gsm_sms_class_init (GsmSmsClass *klass)24 {25 signals[INCOMING_MESSAGE] = g_signal_new (26 "incoming_message",27 G_OBJECT_CLASS_TYPE (klass),28 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,29 0,30 NULL, NULL,31 sms_marshal_VOID__STRING_STRING_BOXED,32 G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING,33 sms_get_features_type());34 }3536 gboolean gsm_sms_send_message (GsmSms *obj, const char *number, const char *contents,37 GHashTable *featuremap, int *ret, GError **error)38 {39 printf("number=%s\n", number);40 printf("contents=%s\n", contents);41 sms_show_features(featuremap);42 *ret = strlen(contents);43 return TRUE;44 }4546 void gsm_sms_emit_incoming_message(GsmSms *obj, const char * address,47 const char * contents, GHashTable *hash)48 {49 g_signal_emit (obj, signals[INCOMING_MESSAGE], 0, address, contents, hash);50 }在類初始化函數gsm_sms_class_init中,我們調用g_signal_new創(chuàng)建了信號。g_signal_new函數的原型是:
guint g_signal_new (const gchar *signal_name,GType itype,GSignalFlags signal_flags,guint class_offset,GSignalAccumulator accumulator,gpointer accu_data,GSignalCMarshaller c_marshaller,GType return_type,guint n_params,...);31行提供了列集函數。32-33行是返回值類型和參數類型。其中第三個參數調用了函數sms_get_features_type(在sms_features.h中聲明)。 因為a{sv}類型的參數處理起來比較繁瑣,我專門寫了一個sms_features模塊處理,后面會介紹。
在主程序中登記對象信息時,對象信息把SendMessage方法和gsm_sms_send_message函數以及自動生成的散集函數聯(lián)系起來。 當客戶程序調用SendMessage方法時,dbus-glib會通過對象信息表格找到回調函數和散集函數, 用散集函數從method_call消息中取出參數傳入回調函數gsm_sms_send_message。 gsm_sms_send_message調用sms_show_features函數處理a{sv}參數。 sms_show_features也在sms_features模塊定義,后面會介紹。
gsm_sms模塊提供了一個gsm_sms_emit_incoming_message函數供外部模塊調用。 調用這個函數可以發(fā)射一個信號。在真實環(huán)境中,只有外部事件發(fā)生后才會發(fā)射信號。 本例中會有測試代碼發(fā)射信號。
3 主程序
3.1 登記dbus服務器
下面就是主程序
$ cat -n smss.c1 #include <dbus/dbus-glib.h>2 #include <stdio.h>3 #include <stdlib.h>4 #include <glib/giochannel.h>5 #include "gsm_sms.h"6 #include "smss-glue.h"7 #include "sms_features.h"89 #define SMSS_DEBUG1011 static void lose (const char *str, ...)12 {13 va_list args;14 va_start (args, str);15 vfprintf (stderr, str, args);16 fputc ('\n', stderr);17 va_end (args);18 exit (1);19 }2021 static void lose_gerror (const char *prefix, GError *error)22 {23 if (error) {24 lose ("%s: %s", prefix, error->message);25 }26 else {27 lose ("%s", prefix);28 }29 }3031 static void shell_help(void)32 {33 printf( "\ts\tsend signal\n"34 "\tq\tQuit\n"35 );36 }3738 void emit_signal(GsmSms *obj)39 {40 GHashTable *features = sms_create_features("ucs2", 3, 1);41 gsm_sms_emit_incoming_message(obj, "12345678901", "hello signal!", features);42 sms_release_features(features);43 }4445 #define STDIN_BUF_SIZE 102446 static gboolean channel_cb(GIOChannel *source, GIOCondition condition, gpointer data)47 {48 int rc;49 char buf[STDIN_BUF_SIZE+1];50 GsmSms *obj = (GsmSms *)data;5152 if (condition != G_IO_IN) {53 return TRUE;54 }5556 /* we've received something on stdin. */57 printf("# ");58 rc = fscanf(stdin, "%s", buf);59 if (rc <= 0) {60 printf("NULL\n");61 return TRUE;62 }6364 if (!strcmp(buf, "h")) {65 shell_help();66 } else if (!strcmp(buf, "?")) {67 shell_help();68 } else if (!strcmp(buf, "s")) {69 emit_signal(obj);70 } else if (!strcmp(buf, "q")) {71 exit(0);72 } else {73 printf("Unknown command `%s'\n", buf);74 }75 return TRUE;76 }7778 int main (int argc, char **argv)79 {80 DBusGConnection *bus;81 DBusGProxy *bus_proxy;82 GError *error = NULL;83 GsmSms *obj;84 GMainLoop *mainloop;85 guint request_name_result;86 GIOChannel *chan;8788 #ifdef SMSS_DEBUG89 g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);90 #endif91 g_type_init ();9293 dbus_g_object_type_install_info (GSM_SMS_TYPE, &dbus_glib_gsm_sms_object_info);9495 mainloop = g_main_loop_new (NULL, FALSE);9697 bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);98 if (!bus)99 lose_gerror ("Couldn't connect to system bus", error);100101 bus_proxy = dbus_g_proxy_new_for_name (bus, "org.freedesktop.DBus",102 "/", "org.freedesktop.DBus");103104 if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error,105 G_TYPE_STRING, "org.freesmartphone.ogsmd",106 G_TYPE_UINT, 0,107 G_TYPE_INVALID,108 G_TYPE_UINT, &request_name_result,109 G_TYPE_INVALID))110 lose_gerror ("Failed to acquire org.freesmartphone.ogsmd", error);111112 obj = g_object_new (GSM_SMS_TYPE, NULL);113 dbus_g_connection_register_g_object (bus, "/org/freesmartphone/GSM/Device", G_OBJECT (obj));114115 printf ("service is running\n");116 chan = g_io_channel_unix_new(0);117 g_io_add_watch(chan, G_IO_IN, channel_cb, obj);118 g_main_loop_run (mainloop);119120 exit (0);121 }93行調用dbus_g_object_type_install_info登記GsmSms類的接口信息。97行連接會話總線。 101-102行在會話總線上為連接"org.freedesktop.DBus"的"/“對象的接口"org.freedesktop.DBus"建立代理。 104-109行通過接口代理調用"RequestName"方法,請求公共名"org.freesmartphone.ogsmd”。
請求公共名成功后,112行建立GsmSms對象。113行登記GsmSms對象,登記時指定對象路徑"/org/freesmartphone/GSM/Device", 并傳入對象指針。118行進入主循環(huán)等待客戶消息。
3.2 IO Channel
我想增加一個敲鍵測試信號發(fā)射。但我又必須在glib主循環(huán)里等待dbus消息。怎樣才能既等待dbus消息,又等待敲鍵呢? 這種情況可以使用glib的IO Channels。glib的IO Channels允許我們在glib主循環(huán)等待指定的文件或socket句柄。
要使用IO Channels,首先包含"glib/giochannel.h"。116行用句柄0(即標準輸入)創(chuàng)建一個GIOChannel。 117行為我們創(chuàng)建的GIOChannel登記回調函數。我們在回調函數channel_cb中處理敲鍵,發(fā)射信號。
3.3 編譯運行
讀者可以從這里下載完整的示例程序。 下集會介紹本例的autotool工程。目前,我們先編譯運行一下,解壓后執(zhí)行:
$ ./configure $ make $ cd src $ ./smss service is running h # s send signalq Quit鍵入h后回車,可以看到敲鍵的幫助信息。
我想找個客戶程序測試一下,dbus-send不能發(fā)a{sv}這樣復雜的參數。我們可以用d-feet測試,或者寫個python腳本:
$ cat ./smsc.py #!/usr/bin/env python import dbus bus=dbus.SessionBus() bus_obj=bus.get_object('org.freesmartphone.ogsmd', '/org/freesmartphone/GSM/Device') iface=dbus.Interface(bus_obj, 'org.freesmartphone.GSM.SMS') ret=iface.SendMessage('1234567890', 'hello from python', {'alphabet':'gsm','csm_num':8,'csm_seq':2}) print "SendMessage return %d" % (ret)執(zhí)行smsc.py,在服務器端看到:
$ ./smss service is running h # s send signalq Quit number=1234567890 contents=hello from python csm_num=8 alphabet=gsm csm_seq=2說明服務器可以正常工作。主程序的89行要求glib直接用malloc分配內存,這樣用valgrind才能檢查到內存泄漏(如果有的話)。 我們可以這樣運行smss以檢查是否有內存泄漏:
$ valgrind --tool=memcheck --leak-check=full ./smss
這一片看得我有一點懵逼,有些代碼結構作者貌似沒講
參考文章:dbus實例講解(四上):使用dbus-glib
總結
以上是生活随笔為你收集整理的linux 进程间通信 dbus-glib【实例】详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Makefile Autotools使用
- 下一篇: Name Error Connectio