基于DBus的进程间通信(IPC)
原文地址:http://sy198704.is-programmer.com/posts/33060.html
最近在研究進程間通信,linux下進程間通信的方式主要有Pipe(管道),FIFO(命名管道),信號,共享內存,消息隊列,信號燈等,這些方式各有各得特點,如管道是linux下命令行中常用的,用于父子進程的通信。但是這些通信方式都比較原始,要屬功能最強大的IPC應該是dbus,故查看了一下dbus的資料,但是資料相對較少,特別是有關python的部分。?
1.dbus概念
網上有一篇叫“D-Bus?Tutorial”的文章,流傳較廣。
D-Bus是針對桌面環境優化的IPC(interprocess?communication?)機制,用于進程間的通信或進程與內核的通信。最基本的D-Bus協議是一對一的通信協議。但在很多情況下,通信的一方是消息總線。消息總線是一個特殊的應用,它同時與多個應用通信,并在應用之間傳遞消息。下面我們會在實例中觀察消息總線的作用。消息總線的角色有點類似與X系統中的窗口管理器,窗口管理器既是X客戶,又負責管理窗口。
支持dbus的系統都有兩個標準的消息總線:系統總線和會話總線。系統總線用于系統與應用的通信。會話總線用于應用之間的通信。網上有一個叫d-feet的python程序,我們可以用它來觀察系統中的dbus世界。
圖1、由d-feet觀察到的D-Bus世界
dbus還提供了兩個命令行工具用于dbus測試,dbus-send和dbus-monitor,前一個命令用于測試信號的發送,后一個命令用于監控dbus的數據流。
2.dbus概念
有關dbus的基礎知識不在本文的范圍內,具體的參見dbus的文檔。下面給出dbus常用的流程。
2.1建立服務的流程
dbus_bus_get(),建立一個dbus連接;
dbus_bus_request_name(),為這個dbus連接(DbusConnection)起名,這個名字將會成為我們在后續進行遠程調用的時候的服務名;
然后我們進入監聽循環?--?dbus_connection_read_write();
從總線上取出消息?--?dbus_connection_pop_message();
并通過比對消息中的方法接口名和方法名?--?dbus_message_is_method_call();
如果一致,那么我們跳轉到相應的處理中去;
在相應的處理中,我們會從消息中取出遠程調用的參數。并且建立起回傳結果的通路?--?reply_to_method_call()。回傳動作本身等同于一次不需要等待結果的遠程調用。
2.2建立服務的流程
建立好dbus連接之后,為這dbus連接命名,申請一個遠程調用通道?--?dbus_message_new_method_call(),注意,在申請遠程調用通道的時候,需要填寫服務器名,本次調用的接口名,和本次調用名(方法名)。壓入本次調用的參數?--?dbus_message_iter_init_append();?dbus_message_iter_append_basic(),實際上是申請了一個首地址,我們就是把我們真正要傳的參數,往這個首地址里面送(送完之后一般都會判斷是否內存越界了)。然后就是啟動發送調用并釋放發送相關的消息結構?--?dbus_connection_send_with_reply()。這個啟動函數中帶有一個句柄。我們馬上會阻塞等待這個句柄給我們帶回總線上回傳的消息。當這個句柄回傳消息之后,我們從消息結構中分離出參數。用dbus提供的函數提取參數的類型和參數?--?dbus_message_iter_init();?dbus_message_iter_next();?dbus_message_iter_get_arg_type();?dbus_message_iter_get_basic()。也就達成了我們進行本次遠程調用的目的了。
2.3發送信號的流程
建立一個dbus連接之后,為這個dbus連接起名,建立一個發送信號的通道,注意,在建立通道的函數中,需要我們填寫該信號的接口名和信號名?--?dbus_message_new_signal()。然后我們把信號對應的相關參數壓進去?--?dbus_message_iter_init_append();?dbus_message_iter_append_basic()。然后就可以啟動發送了?--?dbus_connection_send();?dbus_connection_flush。
2.4信號接收流程
建立一個dbus連接之后,為這個dbus連接起名,為我們將要進行的消息循環添加匹配條件(就是通過信號名和信號接口名來進行匹配控制的)?--?dbus_bus_add_match()。我們進入等待循環后,只需要對信號名,信號接口名進行判斷就可以分別處理各種信號了。在各個處理分支上。我們可以分離出消息中的參數。對參數類型進行判斷和其他的處理。
3.?一個C語言的示例代碼
網上大部分代碼都是基于dbus的一個封裝庫libdbus做的,以及使用glib,gtk的事件循環;為了減少庫的依賴,直接使用C語言調用dbus的底層函數編寫一個遠程調用的示例代碼,代碼很簡單,沒使用GObject等一些復雜的庫。
遠程調用的服務器代碼,用于監控,代碼如下:
?| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 | #include?<dbus/dbus.h>#include?<stdbool.h>#include?<unistd.h>#include?<stdio.h>#include?<stdlib.h>voidreply_to_method_call(DBusMessage*?msg,?DBusConnection*?conn){DBusMessage*?reply;DBusMessageIter?args;boolstat?=?true;dbus_uint32_t?level?=?21614;dbus_uint32_t?serial?=?0;char*?param?=?"";//?read?the?argumentsif(!dbus_message_iter_init(msg,?&args))fprintf(stderr,?"Message?has?no?arguments!\n");?elseif(DBUS_TYPE_STRING?!=?dbus_message_iter_get_arg_type(&args))?fprintf(stderr,?"Argument?is?not?string!\n");?elsedbus_message_iter_get_basic(&args,??m);printf("Method?called?with?%s\n",?param);//?create?a?reply?from?the?messagereply?=?dbus_message_new_method_return(msg);//?add?the?arguments?to?the?replydbus_message_iter_init_append(reply,?&args);if(!dbus_message_iter_append_basic(&args,?DBUS_TYPE_BOOLEAN,?&stat))?{?fprintf(stderr,?"Out?Of?Memory!\n");?exit(1);}if(!dbus_message_iter_append_basic(&args,?DBUS_TYPE_UINT32,?&level))?{?fprintf(stderr,?"Out?Of?Memory!\n");?exit(1);}//?send?the?reply?&&?flush?the?connectionif(!dbus_connection_send(conn,?reply,?&serial))?{fprintf(stderr,?"Out?Of?Memory!\n");?exit(1);}dbus_connection_flush(conn);//?free?the?replydbus_message_unref(reply);}staticvoidreply_to_Introspect(DBusMessage*?msg,?DBusConnection*?conn){/*反饋的消息*/char*xml?=?"<!DOCTYPE?node?PUBLIC?\"-//freedesktop//DTD?D-BUS?Object?Introspection?1.0//EN\"\n\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n""<node>\n""?<interface?name=\"org.freedesktop.DBus.Introspectable\">\n""?<method?name=\"Introspect\">\n""?<arg?name=\"introspection_xml\"?direction=\"out\"?type=\"s\"/>\n""?</method>\n?</interface>\n""?<interface?name=\"test.method.Type\">\n""?<method?name=\"Method\">\n""?<arg?name=\"level\"?direction=\"out\"?type=\"i\"/>\n""?<arg?name=\"serial\"?direction=\"out\"?type=\"i\"/>\n""?</method>\n""?</interface>\n""</node>\n";DBusMessage*?reply;DBusMessageIter?args;boolstat?=?true;//?create?a?reply?from?the?messagereply?=?dbus_message_new_method_return(msg);//?add?the?arguments?to?the?replydbus_message_iter_init_append(reply,?&args);if(!dbus_message_iter_append_basic(&args,?DBUS_TYPE_STRING,?&xml))?{?printf("Dbus?Error:?append?args?error\n");dbus_message_unref(reply);return;}//?send?the?reply?&&?flush?the?connectionif(!dbus_connection_send(conn,?reply,?NULL))?{printf("Dbus?Error:?send?error\n");dbus_message_unref(reply);return;}dbus_connection_flush(conn);//?free?the?replydbus_message_unref(reply);}/***?Server?that?exposes?a?method?call?and?waits?for?it?to?be?called*/voidlisten()?{DBusMessage*?msg;DBusMessage*?reply;DBusMessageIter?args;DBusConnection*?conn;DBusError?err;intret;char*?param;printf("Listening?for?method?calls\n");//?initialise?the?errordbus_error_init(&err);//?connect?to?the?bus?and?check?for?errorsconn?=?dbus_bus_get(DBUS_BUS_SESSION,?&err);if(dbus_error_is_set(&err))?{?fprintf(stderr,?"Connection?Error?(%s)\n",?err.message);?dbus_error_free(&err);?}if(NULL?==?conn)?{fprintf(stderr,?"Connection?Null\n");?exit(1);?}//?request?our?name?on?the?bus?and?check?for?errorsret?=?dbus_bus_request_name(conn,?"test.method.server",?DBUS_NAME_FLAG_REPLACE_EXISTING?,?&err);if(dbus_error_is_set(&err))?{?fprintf(stderr,?"Name?Error?(%s)\n",?err.message);?dbus_error_free(&err);}if(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER?!=?ret)?{?fprintf(stderr,?"Not?Primary?Owner?(%d)\n",?ret);exit(1);?}//?loop,?testing?for?new?messageswhile(true)?{//?non?blocking?read?of?the?next?available?messagedbus_connection_read_write(conn,?0);msg?=?dbus_connection_pop_message(conn);//?loop?again?if?we?haven't?got?a?messageif(NULL?==?msg)?{?sleep(1);?continue;?}//?check?this?is?a?method?call?for?the?right?interface?&?methodif(dbus_message_is_method_call(msg,?"test.method.Type",?"Method"))?reply_to_method_call(msg,?conn);/*實現反射接口*/if(dbus_message_is_method_call(msg,?"org.freedesktop.DBus.Introspectable",?"Introspect"))?reply_to_Introspect(msg,?conn);//?free?the?messagedbus_message_unref(msg);}}intmain(intargc,?char**?argv){listen();return0;}? |
代碼中很關鍵的一個地方是一個標準接口的實現,該接口雖說無實際意義,僅僅是反射出該session的接口信息,包含各個接口信息和信號信息,但是該信息在python版的dbus中調用很重要,否則python的調用會失敗。
編譯命令如下
?| 1 | gcc?-o?main?main.c?`pkg-config?--cflags?--libs?dbus-1` |
可以用d-feet測試一下:
用dbus-send測試命令如下:
?| 1 | dbus-send?--session?--type=method_call?--print-reply?--dest=test.method.server?/?test.method.Type.Method |
客戶端代碼(及遠程調用的代碼):
?| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 | #include?<dbus/dbus.h>#include?<stdbool.h>#include?<unistd.h>#include?<stdio.h>#include?<stdlib.h>/**?*?Call?a?method?on?a?remote?object?*/voidquery(char*?param)?{?DBusMessage*?msg;?DBusMessageIter?args;?DBusConnection*?conn;?DBusError?err;?DBusPendingCall*?pending;?intret;?boolstat;?dbus_uint32_t?level;?printf("Calling?remote?method?with?%s\n",?param);?//?initialiset?the?errors?dbus_error_init(&err);?//?connect?to?the?system?bus?and?check?for?errors?conn?=?dbus_bus_get(DBUS_BUS_SESSION,?&err);?if(dbus_error_is_set(&err))?{?fprintf(stderr,?"Connection?Error?(%s)\n",?err.message);?dbus_error_free(&err);?}?if(NULL?==?conn)?{?exit(1);?}?//?request?our?name?on?the?bus?ret?=?dbus_bus_request_name(conn,?"test.method.caller",?DBUS_NAME_FLAG_REPLACE_EXISTING?,?&err);?if(dbus_error_is_set(&err))?{?fprintf(stderr,?"Name?Error?(%s)\n",?err.message);?dbus_error_free(&err);?}?if(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER?!=?ret)?{?exit(1);?}?//?create?a?new?method?call?and?check?for?errors?msg?=?dbus_message_new_method_call("test.method.server",?//?target?for?the?method?call?"/test/method/Object",?//?object?to?call?on?"test.method.Type",?//?interface?to?call?on?"Method");?//?method?name?if(NULL?==?msg)?{?fprintf(stderr,?"Message?Null\n");?exit(1);?}?//?append?arguments?dbus_message_iter_init_append(msg,?&args);?if(!dbus_message_iter_append_basic(&args,?DBUS_TYPE_STRING,??m))?{?fprintf(stderr,?"Out?Of?Memory!\n");?exit(1);?}?//?send?message?and?get?a?handle?for?a?reply?if(!dbus_connection_send_with_reply?(conn,?msg,?&pending,?-1))?{?//?-1?is?default?timeout?fprintf(stderr,?"Out?Of?Memory!\n");?exit(1);?}?if(NULL?==?pending)?{?fprintf(stderr,?"Pending?Call?Null\n");?exit(1);?}?dbus_connection_flush(conn);?printf("Request?Sent\n");?//?free?message?dbus_message_unref(msg);?//?block?until?we?recieve?a?reply?dbus_pending_call_block(pending);?//?get?the?reply?message?msg?=?dbus_pending_call_steal_reply(pending);?if(NULL?==?msg)?{?fprintf(stderr,?"Reply?Null\n");?exit(1);?}?//?free?the?pending?message?handle?dbus_pending_call_unref(pending);?//?read?the?parameters?if(!dbus_message_iter_init(msg,?&args))?fprintf(stderr,?"Message?has?no?arguments!\n");?elseif(DBUS_TYPE_BOOLEAN?!=?dbus_message_iter_get_arg_type(&args))?fprintf(stderr,?"Argument?is?not?boolean!\n");?elsedbus_message_iter_get_basic(&args,?&stat);?if(!dbus_message_iter_next(&args))?fprintf(stderr,?"Message?has?too?few?arguments!\n");?elseif(DBUS_TYPE_UINT32?!=?dbus_message_iter_get_arg_type(&args))?fprintf(stderr,?"Argument?is?not?int!\n");?elsedbus_message_iter_get_basic(&args,?&level);?printf("Got?Reply:?%d,?%d\n",?stat,?level);?//?free?reply?dbus_message_unref(msg);?}?intmain(intargc,?char**?argv){char*?param?=?"no?param";?query(param);return0;}? |
執行結果:
Calling?remote?method?with?no?param
Request?Sent
Got?Reply:?1,?21614
4.Pthon調用dbus
?| 123456789 | #!/usr/bin/env?python#?-*-?coding:utf-8?-*-importdbusbus?=dbus.SessionBus()bus_obj?=bus.get_object('test.method.server',?'/')interface?=dbus.Interface(bus_obj,?'test.method.Type')info?=interface.Method()printinfo |
?
?
轉載于:https://blog.51cto.com/qsjming/1208070
總結
以上是生活随笔為你收集整理的基于DBus的进程间通信(IPC)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hdu 2441(ACM(Array C
- 下一篇: 注意Hibernate4在开发当中的一些