Linux进程间通信

来源:互联网 发布:阿里云mx记录值 编辑:程序博客网 时间:2024/06/05 10:12


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
GLib has it

UINT16

G_TYPE_UINT

 

Will be changed to a G_TYPE_UINT16 once
GLib has it

INT32

G_TYPE_INT

 

Will be changed to a G_TYPE_INT32 once
GLib has it

UINT32

G_TYPE_UINT

 

Will be changed to a G_TYPE_UINT32 once
GLib has it

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
dbus_g_proxy_set_interface to invoke methods

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
allocated return values; see below.

同时定义了新的数组类型集合。

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.csdn.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

原创粉丝点击