1. Kernel 层 Binder 驱动 相关结构体解释
binder_work
结构体binder_work
用来描述待处理的工作项, 这些工作项有可能属于一个进程, 也有可能属于某一个进程中的某一个线程.binder_node
结构体binder_node
用来描述一个 Binder 实体对象. 每一个 Service 组件在 Binder 驱动程序中都对应一个 Binder 实体对象, 用来描述它在内核中的状态.binder_ref
由于一个 Binder 实体对象可能会被多个 Client 组件引用, 因此 Binder 驱动程序就使用结构体bindr_ref
来描述这些引用关系. 并且将引用了同一个 Binder 实体对象的所有引用都保存在一个 hash 列表中. 这个 hash 列表通过 Binder 实体对象binder_node
的成员变量 refs 来描述. Binder 驱动程序可以通过这个成员变量就可以知道有哪些 Client 组件引用了同一个 Binder 实体对象.binder_proc
结构体binder_proc
用来描述一个正在使用 Binder 进程间通信机制的进程. 当一个进程调用函数open(/dev/binder)
时, Binder 驱动程序就会为它创建一个binder_proc
结构体. 并将它保存在一个全局的 hash 列表binder_proces
中.binder_thread
结构体binder_thread
用来描述 Binder 线程池中的一个线程. 当一个线程注册到 Binder 驱动程序时, Binder 驱动程序就会为它创建一个binder_thread
结构体. 并且将它的状态初始化为BINDER_LOOPER_STATE_NEED_RETURN
表示该线程需要马上返回到用户空间. 由于一个线程在注册为 Binder 线程时可能还没有准备好去处理进程间通信请求, 因此, 最好返回到用户空间去做准备工作.
其中成员变量struct binder_proc *proc
指向其宿主进程. 一个 Binder 线程的 ID 和状态是通过成员变量int pid
与int looper
来描述的.
(每一个使用了 Binder 进程间通信机制的进程都有一个 Binder 线程池, 用来处理进程间通信请求, 这个 Binder 线程池是由 Binder 驱动程序来维护的.)
binder_transaction
结构体binder_transaction
是用来描述进程间通信过程, 这个过程也被称为一个事务.
成员变量unsigned need_reply
用来区分是同步还是异步.0 为 异步.
成员变量struct binder_thread *from
指向发起事务的线程.
成员变量struct binder_proc *to_proc
和struct binder_thread *to_thread
分别指向负责处理该事务的进程和线程. 又被称为目标进程和目标线程.binder_buffer
结构体binder_buffer
用来描述一个内核缓冲区, 它是用来在进程间传输数据的. 每一个使用 Binder 进程间通信机制的进程在 Binder 驱动中都有一个内核缓冲区列表, 用来保存 Binder 驱动程序为它分配的内核缓冲区.
成员变量struct binder_transaction *transaction
与struct binder_node *target_node
用来描述一个内核缓冲区正在交给哪一个事务以及哪一个 Binder 实体对象使用.
Binder 驱动程序将事务数据保存在一个内核缓冲区中. 然后将它交给目标 Binder 实体对象处理. 而目标 Binder 实体对象再将该内核缓冲区的内容交给相应的 Service 组件处理.
成员变量data
指向一块大小可变的数据缓冲区. 它是真正用来保存通信数据的.binder_transaction_data
结构体binder_transaction_data
用来描述进程间通信过程中所传输的数据.
成员变量target
是一个联合体, 用来描述一个目标 Binder 实体对象或者目标 Binder 引用对象. 那么它的成员变量ptr
就指向与该 Binder 实体对象对应的一个 Service 组件内部的一个弱引用计数对象的地址. 如果它描述的是一个目标 Binder 引用对象, 那么它的成员变量handle
就指向该 Binder 引用对象的句柄值.binder_write_read
应用程序进程打开了设备文件/dev/binder
之后, 需要通过 IO 控制函数 ioctl 来进一步与 Binder 驱动程序进行交互, 因此 Binder 驱动程序就提供了一系列的 IO 控制命令来和应用程序进程通信. 而在这些控制命令中, 最重要的就是BINDER_WRITE_READ
命令了. 这个命令后面说跟的参数就是一个binder_write_read
结构体.
结构体binder_write_read
用来描述进程间通信过程中所传输的数据. 这些数据包括输入数据和输出数据.
成员变量write_size, write_consumed, write_buffer
用来描述输入数据, 即从用户空间传到 Binder 驱动程序的数据.
成员变量read_size, read_consumed, read_buffer
用来描述输出数据, 即从 Binder 驱动程序返回给用户空间的数据, 也是进程间通信的结果数据.
其中write_buffer 与 read_buffer
都是一个数组, 数组的每一个元素都由一个通信系协议代码及通信数据组成, 协议码又分为两种类型, 其中一种是输入缓冲区中write_buffer
中使用的, 成为命令协议码, 另一种是在输出缓冲区read_buffer
中使用的, 成为返回协议码.命令协议码 BinderDriverCommandProtocol
其中BC_TRANSACTION, BC_REPLY
后面跟的通信数据使用一个结构体binder_transaction_data
来描述.
当一个进程请求另外一个进程执行某一个操作时, 源进程就使用命令协议代码BC_TRANSACTION
来请求 Binder 驱动程序将通信数据传递到目标进程, 当目标进程处理完源进程所请求的操作之后, 就使用命令协议代码BC_REPLY
来请求 Binder 驱动程序将结果数据传递给源进程.
当一个线程将自己注册到 Binder 驱动程序之后, 它接着就会使用命令协议码BC_ENTER_LOOPER
来通知 Binder 驱动程序, 它已经准备就绪处理进程间通信请求了,返回协议码 BinderDriverReturnProtocol
返回协议代码BR_TRANSACTION
与BR_REPLY
后面跟的通信数据使用的也是用结构体binder_transaction_data
来描述.
当一个 Client 进程向一个 Service 进程发出进程间通信请求时, Binder 驱动程序就会使用返回协议码BR_TRANSACTION
, 通知该 Server 进程来处理该进程间通信请求. 当 Server 进程处理完该进程间通信请求之后, Binder 驱动程序就会使用返回协议代码BR_REPLY
将进程间通信请求结果数据返回给 Client 进程.
当 Binder 驱动程序接收到应用程序进程给它发送的一个命令协议码BC_TRANSACTION
或者BC_REPLY
时, 它就会使用返回协议代码BR_TRANSACTION_COMPLETE
来通知应用程序进程, 该命令协议代码已经被接收, 正在分发给目标进程或者线程处理.
注:无论是 Client 还是 Server 发给 Binder 驱动程序的都是 BC 开头的协议码, 由 Binder 驱动程序发出的都是 BR 开头的返回协议码.
2. Binder 设备的初始化过程总结
binder_init 方法总结如下
- 分配内存
- 调用 misc_register() 创建 binder 设备
- 放入到 binder_devices 链表
3. Binder 设备的打开过程总结
一个进程在使用 Binder 进程间通信机制之前, 首先要调用函数 open 打开设备文件 /dev/binder
来获得一个文件描述符, 然后才能通过这个文件描述符来和 Binder 驱动进行交互, 继而和其他进程执行 Binder 进程间通信.
binder_open 方法总结如下
- 创建
binder_proc
结构体 proc. - 对 pro 进行初始化.设置其 PID, 优先级等.
- 将 pro 加入到全局列表
binder_proces
中 - 将 pro 保准在参数 filp 的成员变量
private_data
中.
参数 filp 指向一个打开文件结构体, 当进程调用函数 open 打开设备文件
/dev/binder
后, 内核就会返回一个文件描述符给进程, 而这个文件描述符与参数 filp 说指向的打开文件结构体是关联在一起的. 因此, 当进程后面以文件描述符为参数调用函数 mmap 或者 ioctl 来与 Binder 驱动程序交互时, 内核就会将该文件描述符相关的打开文件结构体传递为 Binder 驱动程序, 这时候 Binder 驱动程序就可以通过它的成员变量private_data
来获得在函数binder_open
中为进程创建的binder_proc
结构体proc
.
4. Binder 设备的内存映射过程总结
当进程打开了设备文件 /dev/binder
之后, 还需要调用函数 mmap 把这个设备文件映射到进程的地址空间, 然后才可以使用 Binder 进程间通信机制. 设备文件 /dev/binder
对应的是一个虚拟的设备. 将它映射到进程的地址空间是为了为进程分配内核缓冲区.
mmap 方法总结如下
1.为内核空间地址分配大小.
Binder 驱动程序为进程分配的内核缓冲区有两个地址, 一个是用户空间地址, 由参数 vma 所指向的一个
vm_area_struct
结构体来描述. 另一个是内核空间地址 由变量 area 所指向的一个vm_struct
结构体来描述. 进程通过用户空间地址来访问这块内核缓冲区内的内容, 而 Binder 驱动程序是通过内核空间地址来访问这块内核缓冲区的内容.
- 创建物理页面. 即分配内核缓冲区.一般为 4k 大小. 调用
binder_update_page_range()
方法分别映射到内核地址空间与用户地址空间.
当一个进程使用命令协议
BC_TRANSACTON
或者BC_REPLY
来到与 Binder 驱动程序交互时, 它会从用户空间传递一个binder_transaction_data
结构体给 Binder 驱动程序, 在这个结构体中有一个数据缓冲区与一个偏移数组缓冲区. 这两个缓冲区的内容就是需要拷贝到目标进程的内核缓冲区中的.
5. Framework 层 Binder 库
Android 系统在应用程序框架层中间将各种 Binder 驱动程序操作封装成了一个 Binder 库, 这样进程就可以方便的调用 Binder 库提供的接口来实现进程间通信.
BnInterface 与 BpInterface
在 Binder 库中, Service 组件和 Client 组件分别使用模板类 BnInterface
和 BpInterface
来描述, 其中前者称为 Binder 本地对象, 后者称为 Binder 代理对象.
Binder 库中的 Binder 本地对象和 Binder 代理对象分别对应 Binder 驱动中的 Binder 实体对象与 Binder 引用对象.
BnInterface
模板类BnInterface
继承了BBinder
类,BBinder
类为 Binder 本地对象提供了抽象的进程间通信接口.BBinder
类有两个重要的成员函数transact
与onTransact
. 当一个 Binder 代理对象通过 Binder 驱动程序向一个 Binder 本地对象发出一个进程间通信请求时, Binder 驱动程序就会调用该 Binder 本地对象的成员函数transact
来处理该请求
成员函数onTransact
是由BBinder
的子类, 即 Binder 本地对象类来实现的. 它负责分发与业务相关的进程间通信请求.-
BpInterface
模板类BpInterface
继承了BpRefBase
类, 后者为 Binder 代理对象提供了抽象的进程间通信接口.
BpRefBase
类有一个重要的成员变量mRemote
, 它指向一个BpBinder
对象, 可以通过成员函数remote
来获取.
BpBinder
类实现了BpRefBase
的进程间通信接口. 其中BpBinder
类的成员变量mHandle
是一个整数, 表示一个 Client 组件的句柄值.每一个 Client 组件在Binder 驱动程序中都对应有一个 Binder 引用对象, 而每一个 Binder 引用对象都有一个句柄值, Client 组件就是通过这个句柄值来和 Binder 驱动程序中的 Binder 引用对象建立对应关系的.
BpBinder
类中也有成员函数transact
, 用来向运行在 Server 进程中的 Service 组件发送进程间通信请求. 它会将成员变量mHandle
以及进程间通讯数据发送给 Binder 驱动程序. 这样 Binder 驱动程序就能根据这个句柄值来找到对用的 Binder 引用对象. 继而找到对应的 Binder 实体对象. 最后就可以将进程间通讯数据发送给对应的 Service 组件了.
无论是 BBinder 还是 BpBinder 类, 他们都是通过 IPCThreadState 类来和 Binder 驱动程序交互.
IPCThreadState
对于每一个 Binder 线程来说, 它的内部都有一个 IPCThreadState
对象,通过 IPCThreadState
类的 静态成员函数 self
来获取, 并且调用它的成员函数 transanct
来和 Binder 驱动程序交互.
在 IPCThreadState
类的成员函数 transanct
内部, 与 Binder 驱动程序的交互操作又是通过调用成员函数 talkWithDriver
来实现的, 它一方面负责向 Binder 驱动程序发送进程间通信请求, 另一方面又负责接收来自 Binder 驱动的进程间通信请求.
IPCThreadState
类有一个成员变量 mProcess
, 指向一个 ProcessState
对象. 对于每一个使用了 Binder 进程间通信机制的进程来说, 它的内部都有一个 ProcessState
对象. 它负责打开 Binder 设备. 以及将设备文件 /dev/binder
映射到进程的地址空间. 由于 ProcessState
对象在进程范围内是唯一的, 因此 Binder 线程池中的每一个线程都可以通过它来和 Binder 驱动程序建立连接.
ProcessState
进程中的 ProcessState
对象可以通过 ProcessState
类的静态成员函数 self
来获取. 第一次调用 ProcessState
类的静态成员函数 self
时, Binder 库就会为进程创建一个 ProcessState
对象, 并且调用函数 open
来打开设备文件 /dev/binder
, 接着又调用函数 mmap
将它映射到进程的地址空间. 即请求 Binder 前驱动为进程分配内核缓冲区. 映射完成后, 将得到的内核缓冲区的用户地址就保存在其成员变量 mVMStart
中.
binder_node, binder_ref, BBinder, BpBinder 之间的关系
binder_node
binder_node
在最开始说过, 是一个结构体, 对应着一个 Binder 实体对象. 是在 Binder 驱动程序中创建的. 每一个 Service 组件在 Binder 驱动程序中都对应一个 Binder 实体对象,binder_ref
binder_ref
可以理解为 Binder 引用对象. 同样也是在 Binder 驱动程序中创建并且被用户空间的代理对象所引用.
当 Client 进程第一次引用一个 Binder 实体对象时, Binder 驱动程序就会在内部为它创建一个 Binder 引用对象. Binder 引用对象是运行在内核空间的, 引用了它的 Binder 代理对象运行在用户空间.BBinder
BBinder
可以理解为 Binder 本地对象, 在用户空间创建. 并且运行在 Server 进程中. Binder 本地对象一方面会被运行在 Server 进程中的其他对象引用, 另一方面也会被 Binder 驱动程序中的 Binder 实体对象引用(binder_node)
.BpBinder
BpBinder
可以理解为 Binder 代理对象. 它是在用户空间中创建的, 并且运行在 Client 进程中. 与 Binder 本地对象类似, Binder 代理对象一方面会被运行在 Client 进程中的其他对象引用, 另一方面它也会引用 Binder 驱动程序中的 Binder 引用对象(binder_ref)
. 每一个 Binder 代理对象都是通过一个句柄值来和一个 Binder 引用对象关联.
Server 进程将一个 Binder 本地对象 BBinder 注册到 Service Manager 时, Binder 驱动就会为它创建一个 Binder 实体对象 binder_node, 接下来, 当 Client 通过 Service Manager 进程来查询一个 Binder 本地对象的代理对象接口时, Binder 驱动程序就会为它所对应的 Binder 实体对象 binder_node 创建一个 Binder 引用对象 binder_ref.
在 Client 进程与 Server 进程的一次通信过程中, 一共涉及到了四种类型对象, 分别是
- 位于 Binder 驱动程序中的 Binder 实体对象 binder_node
- 位于 Binder 驱动程序中的 Binder 引用对象 binder_ref
- 位于 Binder 库中的 Binder 本地对象 BBinder
- 位于 Binder 库中的 Binder 代理对象 BpBinder
它们的交互过程可划分为五个步骤
- 运行在 Client 进程中的 Binder 代理对象通过 Binder 驱动程序向运行在 Server 进程中的 Binder 本地对象发出一个进程间通信请求, Binder 驱动程序接着就根据 Client 进程传递过来的 Binder 代理对象的句柄值来找到对应的 Binder 引用对象.
- Binder 驱动程序功能键前面找到的 Binder 引用对象找到对应的 Binder 实体对象, 并且创建一个
binder_transacation
事务来描述本次进程间通信过程. - Binder 驱动程序根据前面找到的 Binder 实体对象来找到运行在 Server 进程中的 Binder 本地对象, 将 Client 传来的数据交给它处理.
- Binder 本地对象处理完 Client 进程的通信请求之后, 就将结果返回给 Binder 驱动程序. Binder 驱动程序接着就找到前面所创建的一个事务.
- Binder 驱动程序根据前面找到的事务的相关属性来找到发出通信请求的 Client 进程, 并且通知 Client 进程将通信结果返回给对应的 Binder 代理对象处理.
从这五个过程可以看出, Binder 代理对象依赖于 Binder 引用对象. 而 Binder 引用对象又依赖于 Binder 实体对象. 最后 Binder 实体对象又依赖于 Binder 本地对象.
学习资料参考: 老罗的 Android 系统源代码情景分析