原文地址: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等一些复杂的库。

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

?
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 <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 arguments
if(!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"); 
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);
}
staticvoid
reply_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 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
*/
voidlisten() 
{
DBusMessage* msg;
DBusMessage* reply;
DBusMessageIter args;
DBusConnection* conn;
DBusError err;
intret;
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);
}
}
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

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

?
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 <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"); 
else
dbus_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"); 
else
dbus_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

?
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python
# -*- coding:utf-8 -*-
importdbus
bus =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)相关推荐

  1. 详解操作系统之进程间通信 IPC (InterProcess Communication)

    进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法. 进程是计算机系统分配资源的最小单位(严格说来是线程).每个进程都有 ...

  2. 漫谈QNX(架构/进程,线程,同步,进程间通信IPC)

    (1)架构 说起Blackberry的QNX操作系统, 想必大家都听说过,但到底为什么QNX能如此有名?难道微软的Windows和Linux都不能与之抗衡? 美国NASA的太空接驳飞船也使用QNX操作 ...

  3. linux:进程间通信 IPC

    文章目录 1.管道 1.1.匿名管道 1.2.有名管道 2.信号 3.共享内存 3.1.共享内存接口 3.1.1.生成 key 值 3.1.2.创建共享内存 3.1.3.创建共享内存映射 3.1.4. ...

  4. linux进程间通信 ipc,进程间通信IPC (InterProcess Communication)

    一.进程间通信的概念 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区, ...

  5. Linux进程+进程间通信IPC

    一 Linux进程 1) 进程的内存映像 2)解释 BSS段:在采用段式内存管理的架构中,BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Bloc ...

  6. 进程间通信 IPC、LPC、RPC

    原文请见:进程间通信IPC.LPC.RPC 进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法.进程是计算机系统分配资源 ...

  7. Android 基于Message的进程间通信 Messenger完全解析

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/47017485: 本文出自:[张鸿洋的博客] 一.概述 说到Android进程间 ...

  8. Android中进程间通信(IPC)方式总结

    IPC为进程间通信或跨进程通信,是指两个进程进行进程间通信的过程.在PC和移动设备上一个进程指的是一个程序或者一个应用,所以我们可以将进程间通信简单理解为不同应用之间的通信,当然这种说法并不严谨. 在 ...

  9. Linux系统编程学习笔记(九)进程间通信IPC

    进程间通信IPC: 我们以前介绍过进程控制原语,看到怎么创建多个进程.但是进程之间交互信息的方式只介绍了通过fork或者exec继承父进程的打开文件或者通过文件系统. 经典的进程通信方式有:管道.FI ...

最新文章

  1. 计算机组成 试题,计算机组成典型试题及答案
  2. C# IP地址与数字之间的互转
  3. 异常 Cannot resolve class or package
  4. 查找数据挖掘的相关资料
  5. 2021-11-22--中标麒麟-Linux系统扩容根目录磁盘空间
  6. java guava限流,Guava的RateLimiter实现接口限流
  7. Android Studio(7)---从模板添加代码
  8. ++i和i++哪个效率更高
  9. spss和python stata matlab_(SPSS,Matlab,stata,Python)相关性?
  10. 谢希仁编著《计算机网络》1-6章汇总
  11. CodeCanyon上的20种最佳WordPress登录表单
  12. 什么是区块链BaaS平台?
  13. 前端面试送命题-JS三座大山
  14. Graphics2D 使用详解 【转】
  15. java cookie能存到服务器_Cookie技术用于将会话过程中的数据保存到( )中,从而使浏览器和服务器可以更好地进行数据交互。(5.0分)_学小易找答案...
  16. 诺兰回归,方舟渡劫——短信登录京东青龙
  17. Error(1.0.5 1107071739): D:\SAE_SDK_Windows_1.0.5\apps\/divjs/1/config.yaml is not existed解决方法...
  18. oracle 追究,ORACLE事件跟踪
  19. 2015阿里看雪移动安全挑战赛-第二题
  20. 中医药文化代表东方的思维方式是智慧结晶

热门文章

  1. php 屏蔽ip段,php禁止ip段的方法
  2. 焊接件技术要求怎么写_专硕论文写作要求有高么?具体怎么写?
  3. scheduled每天下午1点执行一次_在Spring Boot项目中使用@Scheduled注解实现定时任务...
  4. nginx访问本地目录一直不好使_nginx 配置根目录不生效问题
  5. 四层负载均衡与七层负载均衡
  6. 编程实现有关SMS4的2个程序之——编程实现线性变换模块
  7. 文件系统写入100G文件需要多久
  8. nginx配置ssl(配置文件)
  9. 从实例入手学会BeautifulSoup的常用方法
  10. The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone