内容摘自:一篇文章了解 Android Binder 进程间通讯机制
什么是 Binder ?
Binder 是 Android 系统进程间通信(IPC)的一种方式。译文为 “粘合剂” ,它的作用也和粘合剂一样,将系统中各个组件(如四大件)粘合到了一起,是各个组件之间的桥梁。
Binder 架构
- Binder 通信采用 C/S 架构。
- Binder 在 framework 层进行了封装,通过 JNI 技术调用 Native 层的 Binder 架构
- Binder 在 Native 层以 ioctl 的方式与 Binder 驱动通讯
Binder 机制
- 首先要注册服务端,服务端通过 ServiceManager 注册服务
- 向 Binder 驱动的全局链表 binder_procs 中插入服务端的信息
- 向 ServiceManager 的 svcinfo 列表中缓存一下注册的服务
svcinfo 列表保存了所有已注册服务的信息
- 客户端获取服务,拿到服务的代理(也可以理解为引用)
获取方式:通过 ServiceManager 从 svcinfo 列表中查询,返回服务端的代理
- 通过 BinderProxy 将客户端请求参数发送给 ServiceManager
- 通过共享内存的方式使用内核方法 copy_from_user() 将参数拷贝到内核空间
- 客户端进入等待状态,Binder 驱动向服务端的 todo 列表中添加一条事务,执行完成之后把执行结果通过 copy_to_user() 将内核的结果拷贝到用户空间
- 唤醒等待的客户端,把结果响应返回
Binder 驱动
先了解一些基本概念。
系统调用
用户空间访问内核空间的唯一方式就是系统调用。
内核态/用户态
当一个进程(任务)执行系统调用而进入内核代码中执行时,就称进程处于内核运行态,简称内核态。此时处理器处于最高特权级(0级)的内核代码中运行。
当进程执行用户自己的代码时,称其处于用户运行态,简称用户态,此时处理器处于最低特权级(3级)的用户代码中运行
驱动
驱动程序一般指的是设备驱动程序(Device Driver),是一种可以使计算机和设备通信的特殊程序。相当于硬件的接口,操作系统只有通过这个接口,才能控制硬件设备的工作;
如上图:
- 户空间中的 binder_open(),binder_mmap(),binder_ioctl()等方法通过 system call 来调用内核空间 Binder 驱动中的方法。
- 内核空间与用户空间通过 copy_from_user(),copy_to_user() 这两个内核方法来完成用户空间与内核空间内存的数据传输。
- Binder 驱动中有一个全局链表 binder_procs 保存服务端的进程信息
ServiceManager
通过 ServiceManager 可以与 Binder 驱动进行通讯。
ServiceManager的作用很简单:提供注册服务和查询服务的功能。
下图为 ServiceManager 启动的过程。
- ServiceManager 分为 framework 层和 native 层,framework 层只是对 native 层进行了封装,方便调用。
- 系统开机时,init 进程解析 init.rc 文件调用 service_manager.c 中的 main() 方法启动 ServiceManager 服务。
- ServiceManager 启动步骤:
- 打开驱动创建全局链表 binder_procs
- 将自己当前进程信息保存到 binder_procs 链表
- 开启 loop 不断处理共享内存中的数据
通过 ServiceManager 注册服务
- 注册服务端:通过 ServiceManager 的 addService() 方法注册服务
- ServiceManager 向 Binder 驱动发送 BC_TRANSACTION 命令(binder client)并携带 ADD_SERVICE_TRANSACTION命令。同时,注册服务的线程进入等待状态,waitForResponse()
- Binder 驱动收到请求,向 ServiceManager 的 todo 队列里面添加一条事务:创建服务端进程 binder_node 信息并插入到 binder_procs 链表中
- 事务处理完,发送 BR_TRANSACTION 命令,ServiceManager 收到命令后向 svcinfo 列表添加已经注册的服务。
- 发送BR_REPLAY(binder replay)唤醒等待的线程,通知注册成功。
完整通信过程
- 通过 ServiceManager 获取到服务端的 BinderProxy 代理对象,通过调用 BinderProxy 将参数、方法标识(例如:TRANSACTION_test)传给 ServiceManager,同时客户端线程进入等待状态。
- ServiceManager 将用户空间的参数等请求数据复制到内核空间
- 向服务端插入一条执行执行方法的事务
- 事务执行完通知 ServiceManager 将执行结果从内核空间复制到用户空间,并唤醒等待的线程,响应结果,通讯结束。