Linux进程间通信
1 目的
对比进程间通信方式,选择适合平台开发的进程间通信方法。
如果是开源模块的话,重点考察通信机制,接口封装程度,稳定性,使用是否简易。
2 Linux常用的进程通信方式简介
2.1 管道
管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
2.2 信号
信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);
2.3 报文
报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
2.4 共享内存
共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
2.5 信号量
信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
2.6 Socket
套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
2.7 小结
此处只是简单介绍了Linux内核提供的进程间通信的方式,具体使用方法可以参照《linux环境进程间通信(全)》
附件:
3 D-bus简介
3.1 主要作用
1. 在同一个桌面会话中,进行桌面应用程序之间的通讯
2. 桌面程序与内核或者守护进程的通信。
3.2 进程通信体系
它有以下几层:
1. libdbus库,提供给各个应用程序调用,使应用程序具有通信和数据交换的能力,两个应用程序可以直接进行通信,就像是一条socket通道,两个程序之间建立通道之后,就可以通讯了。
2. 消息守护进程,在libdbus的基础上创建,可以管理多个应用程序之间的通信。每个应用程序都和消息守护进程建立dbus的链接,然后由消息守护进程进行消息的分派。
3. 各种包装库,有libdbus-glib,libdbus-qt等等,目的是将dbus的底层api进行一下封装。
在DBus的体系中,有一个常驻的进程 Daemon,所有进程间的交互都通过它来进行分发和管理。所有希望使用 DBus 进行通信的进程,都必须事先连上 Daemon,并将自己的名字注册到 Daemon 上,之后,Daemon会根据需要把消息以及数据发到相应的进程中。
3.3 机制简介
D-Bus机制的重要概念有以下几个:
a) 对象:对象是封装后的匹配器与回调函数,它以对等(peer-to-peer)协议使每个消息都有一个源地址和一个目的地址。这些地址又称为对象路径,或者称之为总线名称。对象的接口是回调函数,它以类似C++的虚拟函数实现。当一个进程注册到某个总线时,都要创建相应的消息对象。
b) 消息:D-Bus的消息分为信号(signals)、方法调用(methodcalls)、方法返回(method returns)和错误(errors)。信号是最基本的消息,注册的进程可简单地发送信号到总线上,其他进程通过总线读取消息。方法调用是通过总线传递参数,执行另一个进程接口函数的机制,用于某个进程控制另一个进程。方法返回是注册的进程在收到相关信息后,自动做出反应的机制,由回调函数实现。错误是信号的一种,是注册进程错误处理机制之一。
c) 服务:服务(Services)是进程注册的抽象。进程注册某个地址后,即可获得对应总线的服务。D-Bus提供了服务查询接口,进程可通过该接口查询某个服务是否存在。或者在服务结束时自动收到来自系统的消息。
D-Bus机制描述:
a) D-Bus具备自身的协议,协议基于二进制数据设计,与数据结构和编码方式无关。该协议无需对数据进行序列化,保证了信息传递的高效性。无论是libdbus,还是D-Bus总线守护进程,均不需要太大的系统开销。
b) D-Bus 本身是构建在 Socket 机制之上。真正的通信还是由Socket 来完成的。DBus 则是在这之上,制定了一些通信的协议,并提供了更高一层的接口,以更方便应用程序之间进行数据的交互。
c) 总线是D-Bus的进程间通信机制,一个系统中通常存在多条总线,这些总线由D-Bus总线守护进程管理。最重要的总线为系统总线(SystemBus),Linux内核引导时,该总线就已被装入内存。只有Linux内核、Linux桌面环境和权限较高的程序才能向该总线写入消息,以此保障系统安全性,防止有恶意进程假冒Linux发送消息。会话总线(Session Buses)由普通进程创建,可同时存在多条。会话总线属于某个进程私有,它用于进程间传递消息。
D-Bus通信流程描述:
l 方法调用的一般流程:
1.使用不同语言绑定的dbus高层接口,都提供了一些代理对象,调用其他进程里面的远端对象就像是在本地进程中的调用一样。应用调用代理上的方法,代理将构造一个方法调用消息给远端的进程。
2.在DBUS的底层接口中,应用需要自己构造方法调用消息(methodcall message),而不能使用代理。
3.方法调用消息里面的内容有:目的进程的bus name,方法的名字,方法的参数,目的进程的对象路径,以及可选的接口名称。
4.方法调用消息是发送到bus daemon中的。
5.bus daemon查找目标的bus name,如果找到,就把这个方法发送到该进程中,否则,daemon会产生错误消息,作为应答消息给发送进程。
6.目标进程解开消息,在dbus底层接口中,会立即调用方法,然后发送方法的应答消息给daemon。在dbus高层接口中,会先检测对象路径,接口,方法名称,然后把它转换成对应的对象(如GObject,QT中的QObject等)的方法,然后再将应答结果转换成应答消息发给daemon。
7.bus daemon接受到应答消息,将把应答消息直接发给发出调用消息的进程。
8.应答消息中可以包容很多返回值,也可以标识一个错误发生,当使用绑定时,应答消息将转换为代理对象的返回值,或者进入异常。
busdaemon不对消息重新排序,如果发送了两条消息到同一个进程,他们将按照发送顺序接受到。接受进程并需要按照顺序发出应答消息,例如在多线程中处理这些消息,应答消息的发出是没有顺序的。消息都有一个***可以与应答消息进行配对。
l 信号的一般流程如下:
1.当使用dbus底层接口时,信号需要应用自己创建和发送到daemon,使用dbus高层接口时,可以使用相关对象进行发送,如Glib里面提供的信号触发机制。
2.信号包含的内容有:信号的接口名称,信号名称,发送进程的bus name,以及其他参数。
3.任何进程都可以依据”match rules”注册相关的信号,daemon有一张注册的列表。
4.daemon检测信号,决定哪些进程对这个信号感兴趣,然后把信号发送给这些进程。
5.每个进程收到信号后,如果是使用了dbus高层接口,可以选择触发代理对象上的信号。如果是dbus底层接口,需要检查发送者名称和信号名称,然后决定怎么做。
3.4 基于Glib的样例
3.4.1 对比Glib的类型说明
dbus和glib的数据类型映射如下:
D-Bus basic type |
GType |
Free function |
Notes |
BYTE |
G_TYPE_UCHAR |
|
|
BOOLEAN |
G_TYPE_BOOLEAN |
|
|
INT16 |
G_TYPE_INT |
|
Will be changed to a G_TYPE_INT16 once |
UINT16 |
G_TYPE_UINT |
|
Will be changed to a G_TYPE_UINT16 once |
INT32 |
G_TYPE_INT |
|
Will be changed to a G_TYPE_INT32 once |
UINT32 |
G_TYPE_UINT |
|
Will be changed to a G_TYPE_UINT32 once |
INT64 |
G_TYPE_GINT64 |
|
|
UINT64 |
G_TYPE_GUINT64 |
|
|
DOUBLE |
G_TYPE_DOUBLE |
|
|
STRING |
G_TYPE_STRING |
g_free |
|
OBJECT_PATH |
DBUS_TYPE_G_PROXY |
g_object_unref |
The returned proxy does not have an interface set; use |
Container type mappings
dbus数据也有包容器类型,像DBUS_TYPE_ARRAY 和 DBUS_TYPE_STRUCT,dbus的数据类型可以是嵌套的,如有一个数组,内容是字符串的数组集合。
但是,并不是所有的类型都有普通的使用,DBUS_TYPE_STRUCT应该可以包容非基本类型的数据类型。glib绑定尝试使用比较明显的方式进行声明。
D-Bus type signature |
Description |
GType |
C typedef |
Free function |
Notes |
as |
Array of strings |
G_TYPE_STRV |
char ** |
g_strfreev |
|
v |
Generic value container |
G_TYPE_VALUE |
GValue * |
g_value_unset |
The calling conventions for values expect that method callers have |
同时定义了新的数组类型集合。
D-Bus type signature |
Description |
GType |
C typedef |
Free function |
Notes |
ay |
Array of bytes |
DBUS_TYPE_G_BYTE_ARRAY |
GArray * |
g_array_free |
|
au |
Array of uint |
DBUS_TYPE_G_UINT_ARRAY |
GArray * |
g_array_free |
|
ai |
Array of int |
DBUS_TYPE_G_INT_ARRAY |
GArray * |
g_array_free |
|
ax |
Array of int64 |
DBUS_TYPE_G_INT64_ARRAY |
GArray * |
g_array_free |
|
at |
Array of uint64 |
DBUS_TYPE_G_UINT64_ARRAY |
GArray * |
g_array_free |
|
ad |
Array of double |
DBUS_TYPE_G_DOUBLE_ARRAY |
GArray * |
g_array_free |
|
ab |
Array of boolean |
DBUS_TYPE_G_BOOLEAN_ARRAY |
GArray * |
g_array_free |
|
定义了字典类型
D-Bus type signature |
Description |
GType |
C typedef |
Free function |
Notes |
a{ss} |
Dictionary mapping strings to strings |
DBUS_TYPE_G_STRING_STRING_HASHTABLE |
GHashTable * |
g_hash_table_destroy |
|
3.4.2 使用样例
l 网上有一个叫d-feet的python程序,我们可以用它来观察系统中的dbus世界。
图1、由d-feet观察到的D-Bus世界
D-Bus是一个程序。它提供了API。但我们一般不会直接使用dbus的接口。dbus-glib是GTK版本的dbus接口封装。本文假设读者安装了dbus-glib,我安装的是dbus-glib-0.76。后面还会看到,通过python操纵dbus是多么简单。
l 以hello-dbus3-0.1.tar.gz为例(附录有原例子链接),这是一个autotool工程,大家解包后,执行:
./autogen.sh
./configure
make
然后在src目录运行:
./example-service
这时再运行d-feet,连接session bus,在“Bus Name”窗口会看到一个叫“org.fmddlmyy.Test”连接名。
图2、提供D-Bus服务的org.fmddlmyy.Test
选择“org.fmddlmyy.Test”,在右侧窗口点击展开“ObjectPaths”->“/TestObj”->“Interfaces”->“org.fmddlmyy.Test.Basic”->“Methods”,可以看到一个Add方法。双击Add方法,弹出下面这个对话框:
图3、通过D-Bus接口计算1+2=3
在Parameters窗口输入“1,2”,点击“Execute”按钮,然后在“Output”窗口我们看到了输出结果。我们刚刚创建了一个dbus服务并调用了它。
3.4.3 主要流程
l 通过dbus-binding-tool工具,可以将xml定义的接口文件转换成c语言的头文件。
例如:dbus-binding-tool --prefix=test_obj --mode=glib-server--output=testDbus-glue.h ./testDbus.xml
l 引入生成的头文件,实现接口函数。
l 用dbus_g_bus_get得到session bus的连接
l 在这个连接上用dbus_g_proxy_new_for_name函数获得到拥有指定公共名的连接的指定对象的指定接口的代理
l 最后,用dbus_g_proxy_call函数通过接口代理调用接口提供的方法。
3.4.4 主要接口
l 通过glib的代理proxy调用方法
n Function for synchronously invoking a method and receiving replyvalues
gboolean dbus_g_proxy_call (DBusGProxy *proxy,
const char *method,
GError **error,
GType first_arg_type,
...);
n Function for synchronously invoking a method and receiving replyvalues.
gboolean dbus_g_proxy_call_with_timeout (DBusGProxy *proxy,
constchar *method,
int timeout,
GError **error,
GType first_arg_type,
...);
n Sends a method call message as with dbus_g_proxy_begin_call(), butdoes not ask for a reply or allow you to receive one.
void dbus_g_proxy_call_no_reply (DBusGProxy *proxy,
const char *method,
GType first_arg_type,
...);
n Asynchronously invokes a method on a remote interface.
DBusGProxyCall * dbus_g_proxy_begin_call (DBusGProxy *proxy,
const char *method,
DBusGProxyCallNotifynotify,
gpointer user_data,
GDestroyNotify destroy,
GType first_arg_type,
...);
n Asynchronously invokes a method on a remote interface.
DBusGProxyCall* dbus_g_proxy_begin_call_with_timeout
(DBusGProxy *proxy,
const char *method,
DBusGProxyCallNotify notify,
gpointer user_data,
GDestroyNotify destroy,
int timeout,
GType first_arg_type,
...);
n Collects the results of a method call.
gboolean dbus_g_proxy_end_call (DBusGProxy *proxy,
DBusGProxyCall *call,
GError**error,
GType first_arg_type,
...);
n Cancels a pending method call.
void dbus_g_proxy_cancel_call (DBusGProxy *proxy,
DBusGProxyCall*call);
注:函数说明链接
http://dbus.freedesktop.org/doc/dbus-glib/dbus-glib-DBusGProxy.html
3.5 基于Qt的样例
3.5.1 对比QtDBus
注意:Qt源码编译时,需要保证系统中已经安装好dbus,然后Qt编译选项中要选支持dbus,这样会才会生成LibQtDBus.so等相关库文件。
Qt的example中有几个例子,这里就不详述了。
3.6 直接调用dbus接口
使用方式类似:
l 用dbus_bus_get获取session bus的连接
l 通过session bus的连接绑定等待数据
l 发送数据
n 异步函数调用:dbus_connection_send
n 同步函数调用:dbus_connection_send_with_reply
3.7 优点
低延迟,低开销,高可用性:
1) 低延迟:DBus一开始就是用来设计成避免来回传递和允许异步操作的。因此虽然在Application和 Daemon之间是通过socket实现的,但是又去掉了socket的循环等待,保证了操作的实时高效。
2) 低开销:DBus使用一个二进制的协议,不需要转化成像XML这样的文本格式。因为DBus是主要用来机器内部的IPC,而不是为了网络上的IPC机制而准备的.所以它才能够在本机内部达到最优效果。
3) 高可用性:DBus是基于消息机制而不是字节流机制。它能自动管理一大堆困难的IPC问题。同样的,DBus库被设计来让程序员能够使用他们已经写好的代码。而不会让他们放弃已经写好的代码,*通过学习新的IPC机制来根据新的IPC特性重写这些代码。
4 AF_BUS简介
在Genivi库中以补丁的形式存在,分别对linux,glib,dbus进行了部分修改。
目前没有形成demo。
5 预研小结
根据目前现有资料分析,基础的Linux进程间通信方式只能停留在字符序列上,需要进一步封装才能满足使用需求。而D-Bus则基于消息封装了底层实现,接口又有glib、qt等进行再此包装,实现上能做到低延迟,低开销,高可用性,都是其使用优点。
未来使用d-bus还需要留意的几点:
l 它底层是用socket封装的,是否可以轻易的在多主机间通信,如何配置等。
l 在大数据量下,是否还能满足我们的需求。
l 他自身机制和我们应用的进程线程保护之间是否有注意的地方。
6 附录
6.1 D-Bus相关资料
一些基本概念的解释和翻译:
http://blog.mcuol.com/User/AT91RM9200/Article/12816_1.htm
http://www.cnblogs.com/wzh206/archive/2010/05/13/1734901.html
一个完整的DBus学习教程(强烈推荐,写得相当的全):
http://blog.****.net/fmddlmyy/archive/2008/12/23/3585730.aspx
两个DBus的完整示例,相当有参考价值
http://hi.baidu.com/zengzhaonong/blog/item/670b98d6e63ae42c07088bae.html
DBus 官方网站,最原滋原味的DBus学习内容
http://www.freedesktop.org/wiki/Software/dbus
http://dbus.freedesktop.org/doc/dbus-tutorial.html
http://dbus.freedesktop.org/doc/dbus-specification.html
资料推荐出处:
http://blog.sina.com.cn/s/blog_65d6476a0101em1h.html
6.2 D-Bus安装
Ubuntu下安装D-Bus包:
要先装一下libdbus,执行一下以下语句就OK了
sudo apt-get install libgtk2.0-dev
sudo apt-get install libdbus-glib-1-dev
//另可以安装一下下面这个工具
sudo apt-get install d-feet