
Linux | D-Bus Overview
DBus是一种基于Unix Domain Socket的新型IPC,已经实现了很多我们常用的功能。
最近做的事情和DBus有非常多的关系,稍微记录一下这半年一年接触到的DBus的内容。
IPC
IPC全程是进程间Inter Process通信Communication。比如说我们用浏览器,浏览器需要和服务器通信,这个过程中用的是套接字Sockets这种IPC。
比较常见的IPC有
- 文件:对,文件可以用来传输数据,当然可以用作IPC啦
- 管道:一对一的通信手段
- 有名管道/FIFO:通过mkfifo创建的类似于文件的一对一的管道。
- 无名管道:举个例子,
ps aux | grep shed
中,bash会产生ps和grep两个进程,这两个进程就是由无名管道连接的。
- 信号,没错,信号可以传递数据的,但是信号一般是单向的。
- 共享内存,可以映射到进程自己的内存空间中,从而交换信息。
- Semaphore:进线程间同步用的通信手段。
- 套接字:一般有一个监听的一端,有一个连接的一端,是双向的通信手段。
- IP TCP/UDP Socket:我们最常用的IPC手段,不止可以在本机建立信道,在不同主机、不同网络之间也可以建立信道。在这之上可以建立应用层连接。
- Netlink Raw Socket:内核提供的套接字类型,主要是用来在用户空间和内核空间之间通信。举个例子,可以用nl套接字配置网络。
- Unix Domain Socket:文件形式的Socket,一般是本地应用之间通信用,监听的一端可以收到连接端的进程信息。Docker以及后面要说的DBus类应用都是依赖于Unix Domain Socket。
IPC解决了的问题,IPC没解决的问题
现在的系统越来越复杂,越来越难以在一个程序中处理一类事情,不同程序的协作几乎成了必然。IPC的诞生就是解决了不同进程间的通信问题:我们有管道在进程间做管道才能用grep、awk、sed、tr之类的工具处理文本,有文件才可以在本地持久化并且共享信息,有信号才可以及时通知其他进程发生了什么,有套接字才可以在不同主机间通信。
看起来很完美,我们还有什么不满足的?
很久以前我们每写一个程序都要自己发明一套通信手段,每和一个应用接入就要接入一整套SDK,然后建立两个进程间的信道,非常的重而且不可靠,搞不好效率也不高。我们要追求更高的生产力,更少的出错的可能性,就需要一个在大部分时间都通用的协议。由此出发,各路大神发明了非常多的新的IPC手段。
这些新型IPC中,最典型的一类就是RPC,远程过程调用,主要是在不同主机间进行通信的手段。其中出名的包括
- RPC
- xml-rpc,基于XML和http的文本协议
- json-rpc,基于json和http的文本协议
- grpc:基于protobuf和http2/h2c的二进制协议
- IPC/RPC
- dbus: 主要基于unix domain socket的二进制协议
- varlink
其中今天最主要要提到的就是这个dbus。
DBus
dbus在centos6的时候就有了,起源于freedesktop项目,目的是为了统一桌面环境中进程间通信的方式。
通常来说,系统里面会有一个系统dbus服务,socket路径在/var/run/dbus/system_bus_socket
,每个登录也会有自己的dbus服务,通过环境变量DBUS_SESSION_BUS_ADDRESS可以知道具体的路径。
一个进程一般会连接到其中一个bus上,连接以后dbus会分给连接一个unique name,例如:1.1
,觉得这个名字不好看、其他程序不好找,我们也可以起一个有意义的名字,一般是
一个逆着写的域名加上一个具体的名字
,比如说org.freedesktop.system1
。
我们写http服务的时候,一般会在同一个同一个端口上提供同一个服务。但是对于dbus来说,情况可能会更复杂一些,systemd这样的应用需要在dbus上为不同的unit提供不同的对象,每类对象有不同的接口。所以dbus设计上,把服务和对象绑定了起来,我们需要把对象发布到一个dbus的
对象路径
上,以对象为单位给其他应用提供服务,一个对象有多个接口,接口定义具体是做什么的。通常情况下,都会有一个路径每级都和unique name中的部分一一对应的对象,比如说org.freedesktop.systemd1
就有一个位于/org/freedesktop/systemd1
的对象,这个对象有org.freedesktop.DBus.Peer
、org.freedesktop.DBus.Introspectable
、org.freedesktop.DBus.Properties
、org.freedesktop.systemd1.Manager
四个接口,每个接口有不同的函数、信号和属性
大致来说要在dbus里面的要素包括
- 是哪根bus上的服务
- 提供服务的连接名是啥
- 提供服务的对象的路径是啥
- 需要对象上的哪个接口
- 需要的是函数、信号还是属性
- 名字叫啥
用packagekit举个例子,我要列举可以升级的软件包需要:
- 建立一个事务:
- 调用
- system bus上的
- org.freedesktop.PackageKit连接中
- /org/freedesktop/PackageKit对象上的
- org.freedesktop.PackageKit接口上的
- CreateTransaction
- 函数
- 这个调用会返回一个字符串,用来表示transaction所对应的object在连接中的路径
- 调用
- 监听结果
- 监听
- system bus上的
- org.freedesktop.PackageKit连接中
- 刚才返回路径所表示的对象上的
- org.freedesktop.PackageKit.Transaction接口上的
- Package和Finished
- 信号
- 监听
- 让PackageKit获取升级列表
- 调用
- system bus上的
- org.freedesktop.PackageKit连接中
- 刚才返回路径所表示的对象上的
- org.freedesktop.PackageKit.Transaction接口上的
- GetUpdates
- 函数
- 调用
如果需要的话我们也可以获取一些属性,比如说
- 要获取是谁创建的transaction
- system bus上的
- org.freedesktop.PackageKit连接中
- 刚才返回路径所表示的对象上的
- org.freedesktop.PackageKit.Transaction接口上的
- Uid
- 属性
DBus通过这种方法为各种各样的服务提供了一种通用的通信机制。