DBus通讯

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等一些复杂的库。

远程调用的服务器代码,用于监控,代码如下:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

#include

#include

#include

#include

#include

void reply_to_method_call(DBusMessage* msg, DBusConnection* conn)

{

DBusMessage* reply;

DBusMessageIter args;

bool stat = true;

dbus_uint32_t level = 21614;

dbus_uint32_t serial = 0;

char* param = "";

// read the arguments

if (!dbus_message_iter_init(msg, &args))

fprintf(stderr, "Message has no arguments!\n");

else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args))

fprintf(stderr, "Argument is not string!\n");

else

dbus_message_iter_get_basic(&args, ¶m);

printf("Method called with %s\n", param);

// create a reply from the message

reply = dbus_message_new_method_return(msg);

// add the arguments to the reply

dbus_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 connection

if (!dbus_connection_send(conn, reply, &serial)) {

fprintf(stderr, "Out Of Memory!\n");

exit(1);

}

dbus_connection_flush(conn);

// free the reply

dbus_message_unref(reply);

}

static void

reply_to_Introspect(DBusMessage* msg, DBusConnection* conn)

{

/*反馈的消息*/

char *xml = "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"

"\n"

"  \n"

"    \n"

"      \n"

"    \n  \n"

"  \n"

"    \n"

"      \n"

"      \n"

"    \n"

"  \n"

"\n";

DBusMessage* reply;

DBusMessageIter args;

bool stat = true;

// create a reply from the message

reply = dbus_message_new_method_return(msg);

// add the arguments to the reply

dbus_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 connection

if (!dbus_connection_send(conn, reply, NULL)) {

printf ("Dbus Error: send error\n");

dbus_message_unref(reply);

return;

}

dbus_connection_flush(conn);

// free the reply

dbus_message_unref(reply);

}

/**

* Server that exposes a method call and waits for it to be called

*/

void listen()

{

DBusMessage* msg;

DBusMessage* reply;

DBusMessageIter args;

DBusConnection* conn;

DBusError err;

int ret;

char* param;

printf("Listening for method calls\n");

// initialise the error

dbus_error_init(&err);

// connect to the 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) {

fprintf(stderr, "Connection Null\n");

exit(1);

}

// request our name on the bus and check for errors

ret = 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 messages

while (true) {

// non blocking read of the next available message

dbus_connection_read_write(conn, 0);

msg = dbus_connection_pop_message(conn);

// loop again if we haven‘t got a message

if (NULL == msg) {

sleep(1);

continue;

}

// check this is a method call for the right interface & method

if (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 message

dbus_message_unref(msg);

}

}

int main(int argc, char** argv)

{

listen();

return 0;

}

代码中很关键的一个地方是一个标准接口的实现,该接口虽说无实际意义,仅仅是反射出该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

客户端代码(及远程调用的代码):

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

#include

#include

#include

#include

#include

/**

* Call a method on a remote object

*/

void query(char* param)

{

DBusMessage* msg;

DBusMessageIter args;

DBusConnection* conn;

DBusError err;

DBusPendingCall* pending;

int ret;

bool stat;

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");

else if (DBUS_TYPE_BOOLEAN != dbus_message_iter_get_arg_type(&args))

fprintf(stderr, "Argument is not boolean!\n");

else

dbus_message_iter_get_basic(&args, &stat);

if (!dbus_message_iter_next(&args))

fprintf(stderr, "Message has too few arguments!\n");

else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args))

fprintf(stderr, "Argument is not int!\n");

else

dbus_message_iter_get_basic(&args, &level);

printf("Got Reply: %d, %d\n", stat, level);

// free reply

dbus_message_unref(msg);

}

int main(int argc, char** argv)

{

char* param = "no param";

query(param);

return 0;

}

执行结果:

Calling remote method with no param
Request Sent
Got Reply: 1, 21614

4.Pthon调用dbus

?


1

2

3

4

5

6

7

8

9

#!/usr/bin/env python

# -*- coding:utf-8 -*-

import dbus

bus = dbus.SessionBus()

bus_obj = bus.get_object(‘test.method.server‘, ‘/‘)

interface = dbus.Interface(bus_obj, ‘test.method.Type‘)

info = interface.Method()

print info

时间: 2024-08-23 22:48:48

DBus通讯的相关文章

八、Linux精简系统和内核管理裁剪(一)

一.内核 1.什么是内核 内核其实就是操作系统,是驱动计算机硬件,实现人机操作,并提供其他服务器功能的底层系统.linux操作系统也称为"内核",指的是同一件事情. linux的内核是以模块化的方式工作的,主要工作任务包括存储管理.cpu管理.进程管理.文件系统管理.设备驱动管理.网络管理.系统调用.安全管理等. 2.内核的组成部分包括vmlinuz.initrd(linux5)或者initramfs(linux6)./lib/modules(模块,子核心) 二.内核设计 微内核 就是

Linux 下搭建流媒体服务器

http://blog.csdn.net/huangtaishuai/article/details/9836581 本文说明如何在 CentOS 6 服务器(无图形界面)搭建以 VLC 为核心的直播流媒体服务器. 第一步,安装第三方 yum 源.因为官方 yum 源中是不包含 VLC 安装包的,为了不用考虑安装包之间的依赖关系,使用第三方yum源的 yum 安装方式,能极大地简化 vlc 的安装过程,将主要精力集中到如何使用 VLC 功能来搭建流媒体服务器.(如果不想使用 yum 安装的,请自

DBus学习

1. 介绍 DBus是一种桌面环境的进程间通讯(IPC)机制,有低时延.低消耗等优点 基于socket(有待考证),提供了一对一的对等通讯 使用dbus-daemon作为后台进程时,可实现多对多通讯 由如下三个层次 - libdbus: 接口库,提供点对点通信和数据交换的能力  - 守护进程: 即dbus daemon进程,提供多对多的通信机制,进程与daemon建立dbus连接,由daemon进行消息的分发 - 封装库: 特定框架下的封装,如dbus-glib/GDBus, QtDBus Ti

DBus学习笔记

摘要:DBus作为一个轻量级的IPC被越来越多的平台接受,在MeeGo中DBus也是主要的进程间通信方式,这个笔记将从基本概念开始记录笔者学习DBus的过程 [1] DBus学习笔记一:DBus学习的一些参考资料[2] DBus学习笔记二:什么是DBus?[3] DBus学习笔记三:DBus的一些基本概念 一些基本概念的解释和翻译:http://blog.mcuol.com/User/AT91RM9200/Article/12816_1.htmhttp://www.cnblogs.com/wzh

Dbus组成和原理

DBUS是实质上一个适用于桌面应用的进程间的通讯机制,即所谓的IPC机制.适合在同一台机器,不适合于INTERNET的IPC机制.DBUS不是一个为所有可能的应用的通用的IPC机制,不支持其他IPC机制的很多特性.DBUS提供了一个低时延.低消耗的IPC通讯,因为它采用了二进制的数据交换协议,不需要转换成文本化的数据进行交换,DBUS提供了面向多重对象系统的包装,可以在原有的面向对象的应用框架下使用DBUS,不需要学习新的概念和规范等. DBUS是支持一对一和多对多的对等通讯,在一对一的直接通讯

Android网络通讯简介

网络通信应该包含三部分的内容:发送方.接收方.协议栈.发送方和接收方是参与通信的主体,协议栈是发送方和接收方进行通信的契约.按照服务类型,网络通信可分为面向连接和无连接的方式.面向连接是在通信前建立通信链路,而通信结束后释放该链路.无连接的方式则不需要在通信前建立通信连接,这种方式不保证传输的质量. Android提供了多种网络通信的方式,如Java中提供的网络编程,在Android中都提供了支持.Android中常用的网络编程方式如下: 针对TCP/IP协议的Socket和ServerSock

用ESP8266 WIFI模块连接服务器,并实现与服务器相互通讯

最近在做一个智能锁的项目,该项目要求实现在任何地方(当然是要有网络的)可以在手机上用APP开锁.而我负责的部分主要是实现底层与服务器连接,并且要能相互通讯.考虑了很多问题,最终选择了用ESP8266 WIFI模块实现了这个功能.下面向大家就简单分享一下. 工具:网络调试助手  ESP8266  STM32F1开发板 首先,用网络调试助手来虚拟一个服务器,如下: 有了服务器后,接下来我们就要用WIFI来连接这个服务器.ESP8266 有三种工作模式,由于项目要求,我选用了STA中的客户端模式.下面

java SSM框架 多数据源 代码生成器 websocket即时通讯 shiro redis 后台框架源码

获取[下载地址]   QQ: 313596790官网 http://www.fhadmin.org/A 调用摄像头拍照,自定义裁剪编辑头像,头像图片色度调节B 集成代码生成器 [正反双向](单表.主表.明细表.树形表,快速开发利器)+快速表单构建器 freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块C 集成阿里巴巴数据库连接池druid  数据库连接池  阿里巴巴的 druid.Druid在监控.可扩展性.稳定性和性能方面都

线程通讯

/* 线程通讯: 一个线程完成了自己的任务时,要通知另外一个线程去完成另外一个任务. 生产者与消费者 wait(): 等待 如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify方法才能唤醒. notify(): 唤醒 唤醒线程池等待线程其中的一个. notifyAll() : 唤醒线程池所有等待 线程. wait与notify方法要注意的事项: 1. wait方法与notify方法是属于Object对象 的. 2. wait方法与notify方