进程间采用pydbus通信并设置为系统服务

在做智能家居的产品的时候,设备上跑的是个linux系统,里面有一些C/C++的和Python的程序,互相之间需要来回传一些数据,发一些信号。发现dbus这个东西可以用,Python有pydbus,但是在使用过程中,发现官方的文档确实不是那么详实,所以记录一些经验。

python这边的库本来用的是dbus-python,结果后来发现deprecated了。改成pydbus,发现简洁了很多,不过文档就更简略了。

prerequisite:

非需要一个 python3-gi, 关键是这个包在pypi里没有,没法pip安装,所以很烦人,比如在虚拟环境里,就得折腾。如果是python2,那debian系里的包应该叫python-gi。

还需要dbus,估计还需要dbus-x11

python 这边我也推荐使用pydbus。


要建立一个dbus服务,并提供method可以被其他进程调用,就下面这个例子就行:

https://github.com/LEW21/pydbus/blob/master/examples/clientserver/server.py

interface的名字起成和busname一样也没啥事,反正简单需求。

from gi.repository import GLib

这句就是为啥需要那个python3-gi了。

需要它的loop

loop = GLib.MainLoop()

并且在最后让loop无限循环下去:

loop.run()

bus = SessionBus()

bus.publish("net.lew21.pydbus.ClientServerExample", MyDBUSService())

这两句是声明一个Session Bus的连接,然后把自己定义的这个服务发表出去,名字就是那一串字符。

服务类的定义很简单,method主要靠docstring的注释。里面最主要的无非就是args的类型

arg type='s' name='response' direction='out'

out 是表示这是method的返回值,s就是字符串类型,

arg type='s' name='a' direction='in'

in就是method的调用参数,s是字符串,参数名是a,

def EchoString(self, s):

return s

这个method就实现了这个相应的声明。

这个时候你如果能把这个文件运行起来,那么一个发表在dbus上的服务就开始工作了。

要调用这个dbus服务,可以在另外一个进程里,也连到dbus上,然后通过dbus根据名字获得相应的proxy对象,然后用proxy对象来调用函数。

bus = SessionBus()

obj = bus.get('com.pi.mic')

result = obj.EchoString('Hello')


然而。。这是个sessionbus,就是说这个是每个session里的,也只能跟同一个session里的进程通讯。而我的需求是系统里跑的服务,跟用户登不登录有没有界面没关系。。所以我要用SystemBus。

当然也很简单,就是把上面的SessionBus换成SystemBus就行了。

然而。。当你运行的时候,就会发现报权限错误,无法拥有那个bus名字。。

这是因为策略的问题,所以我们需要一个策略文件

https://github.com/LEW21/pydbus/blob/master/examples/polkit/dbus.conf

allow own="net.lew21.pydbus.PolkitExample"

这一句就是允许拥有这个bus名字。但是例子里这个配置是写在user="root" 里的,所以只有root能运行。如果其他用户运行,还是会出现这个错误:GLib.Error: g-dbus-error-quark: GDBus.Error:org.freedesktop.DBus.Error.AccessDenied: Connection ":x.xx" is not allowed to own the service "x.x.x" due to security policies in the configuration file

如果要让其他用户能运行,可以把这一句放到context="default" 里

后面两句:

allow send_destination="net.lew21.pydbus.PolkitExample"

allow receive_sender="net.lew21.pydbus.PolkitExample"

就是允许收发消息了。

这个文件要放到/etc/dbus-1/system.d/  目录下面。

现在,手动执行那个python脚本是可以启动服务了,但是要想把它变成开机自动启动的,还需要加systemd的配置。

https://stackoverflow.com/questions/31702465/how-to-define-a-d-bus-activated-systemd-service

按这篇答案里的方法把两个配置文件写好,应该就可以开机启动了。

有个问题要注意,调用的函数如果执行时间长,调用者会block在那等返回。如果不想这样,可以用异步函数调用方式,加两个参数。

下面的问题是,如果是method,如果客户端要call服务端的method,那么服务端就得在call之前运行起来并且在bus上发布自己,但是如果情况是,你两个进程之间要相互call,比如A有method  a, B有method b,A在某些情况下要call b, B在某些情况下要call a,那么就很苦恼了,当然你可以把bus.get 写在启动以后具体需要调用的函数里。这样不会一启动就依赖另一个服务。或者还有个办法,就是使用信号signal。其实使用signal的主要场景是事件触发。比如A对象会在运行过程中产生一个事件a,他并不想关心其他进程怎么去处理这个事件,所以只需要发出一个signal即可。其他所有对这个事件感兴趣的进程只需要订阅这个signal,绑定回调函数,那么当A发出这个signal时,其他所有订阅了这个signal的进程的回调函数都会被自动调用。

signal的定义很简单,发送方在docstring里和method的声明方式一样,然后

from pydbus.generic import signal

signalname = signal()

这里要注意的是在docstring里声明的时候,signal的参数的direction都是out,

然后在需要订阅signal的进程里,

def hello_signal_handler(hello_string):

    print("Received signal and it says: " +hello_string)

bus = SystemBus()

mainloop = GLib.MainLoop()

obj = bus.get('com.pi.mic')

obj.HelloSignal.connect(hello_signal_handler)

mainloop.run()

解释一下,就是先连上bus,然后get回发出signal的proxy对象,然后声明当收到相应的signal的时候要调用哪个回调函数。由于要接受信号并回调,所以和publish bus一样,需要用loop。

这样信号发送和接收就都写完了。

还剩下一个问题,怎么发出signal呢,只需要在发送进程里按普通调用函数方式,比如在 A 对象里的某个函数里,要发送signal,只需要self.HelloSignal('world'),就行了。

但是这个调用并不能在别的进程里通过proxy对象发,比如我不能在C进程里,obj = bus.get('com.pi.mic'), 然后obj.HelloSignal('world'),这是不行的。

signal还有一个好处就是发送方不需要等待,发送完直接返回,至于订阅的人是怎么执行,以及执行多久,就不是发送方要考虑的事情了。


有时候我们可能想监听更加自由的signal,比如不管是哪个obj的同一个名字的信号,可以用bus的subscribe 方法。这个方法有7个参数。都是可选的。

bus.subscribe(sender=None,iface=None,signal=None,object=None,arg0=None,flags=0,signal_fired=signal_fired)

先定义一个signal_fired 函数,你非要叫callback也行。参数包括 sender, object, iface, signal, params

def signal_fired(sender, object, iface, signal, params):

        print("receive signal of emit event {0}".format("".join(params)))

这里params就是从发送信号的对象那发送的时候给发送函数送的参数,是个元组,如果信号没参数那就是空元组。

然后订阅事件:

这里如果你是要监听任何信号,那就全都不传就行。。。哦,你要想调用回调函数,总得传signal_fired吧。

如果你要监听通过interface为com.pi.event.emit发的信号名字为event_emit_signal的信号,就传这两个,加上signal_fired

bus.subscribe(signal="event_emit_signal", iface='com.pi.event.emit', signal_fired=signal_fired)

这样不管哪个对象发出的信号,只要是interface和signal的名字都对的上就能收到,能调用回调函数。

如果你只想关心信号名字:

bus.subscribe(signal="event_emit_signal", signal_fired=signal_fired)


写这篇的时候的pydbus版本https://github.com/LEW21/pydbus 还不支持异步函数调用。


如果需要c语言版本的dbus server的例子,可以参考 https://github.com/fbuihuu/samples-dbus

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 进程间使用D-Bus通信 D-Bus是一种高级的进程间通信机制,它由freedesktop.org项目提供,使用G...
    WB莫遥燚阅读 21,885评论 0 7
  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,297评论 0 6
  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 4,085评论 0 23
  • 进程间的通信主要分为本机器进程间的通信和不同机器间进程的通信。本文主要描述本机进程间的通信。 一、传统Linux的...
    一叶之界阅读 392评论 0 2