先从内核来总结下Binder驱动,从Binder IPC原理我们就知道了,对于进程来说,用户空间是不能共享的,而内核空间却可以。所以只能费这么大劲,在内核设计一个Binder驱动来间接打通client和server进程的通信。另外,Binder驱动是Android专用的,尽管名叫“驱动“,实际上和硬件设备没有任何关系,只是实现方式和设备驱动程序是一样的而已。
一、系统调用
在学习Binder Driver之前,先了解下系统调用流程:用户态的程序调用Kernel层驱动是需要陷入内核态,进行系统调用(syscall),比如打开Binder驱动方法的调用链为: open()(用户空间方法)-> __open()(systemCall对应方法)-> binder_open()(binder 驱动方法)。至于其他的从用户态陷入内核态的流程也基本一致。
二、主要工作函数介绍
binder_init:注册misc设备,对应于miscdevice结构体。这个不多说,简单说就是驱动的注册以及一些初始化工作。
binder_open:创建binder_proc对象,并把当前进程等信息保存到binder_proc对象,该对象管理IPC所需的各种信息并拥有其他结构体的根结构体;再把binder_proc对象保存到文件指针filp,以及把binder_proc加入到全局链表binder_procs。(Binder驱动中通过static HLIST_HEAD(binder_procs)创建了全局的哈希链表binder_procs。)
binder_mmap: 首先在内核虚拟地址空间,申请一块与用户虚拟内存相同大小的内存;然后再申请1个page大小的物理内存,再将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,从而实现了用户空间的Buffer和内核空间的Buffer同步操作的功能。
binder_ioctl: 针对不同的ioctl命令在两个进程间收发IPC数据和IPC reply数据。
通过switch接收不同的命令,对应执行不同的操作,代码如下:
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
//参数arg表示用户空间传过来的数据
{
...
switch (cmd) {
case BINDER_WRITE_READ: //进行binder的读写操作
ret = binder_ioctl_write_read(filp, cmd, arg, thread); //【见2.4.2】
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS: //设置binder最大支持的线程数
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR: //成为binder的上下文管理者,也就是ServiceManager成为守护进程
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
case BINDER_THREAD_EXIT: //当binder线程退出,释放binder线程
binder_free_thread(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: { //获取binder的版本号
struct binder_version __user *ver = ubuf;
if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
default:
ret = -EINVAL;
goto err;
}
...
return ret;
}
三、Binder协议
Binder协议可以分为控制协议和驱动协议两类。
控制协议:用户空间进程通过ioctl与Binder驱动进行通信的协议,也叫做ioctl命令。
主要控制协议如下:
ioctl命令 | 操作 | 使用场景 |
---|---|---|
BINDER_WRITE_READ | 收发Binder IPC数据 | Binder读写交互场景,IPC.talkWithDriver |
BINDER_SET_MAX_THREADS | 设置Binder线程最大个数 | servicemanager进程成为上下文管理者,binder_become_context_manager() |
BINDER_SET_CONTEXT_MGR | 设置Service Manager节点 | 初始化ProcessState对象,open_driver()主动调整参数,ProcessState.setThreadPoolMaxThreadCount() |
驱动协议:描述了对于Binder驱动的具体使用过程。
驱动协议又可以分为两类:
BINDER_COMMAND_PROTOCOL:binder请求码,以”BC_“开头,简称BC码,用于从IPC层传递到Binder Driver层;
BINDER_RETURN_PROTOCOL :binder响应码,以”BR_“开头,简称BR码,用于从Binder Driver层传递到IPC层;
列举一次完整的Binder通信过程:
BC_PROTOCOL:
请求码 | 参数类型 | 作用 | 调用方法 |
---|---|---|---|
BC_TRANSACTION | binder_transaction_data | Client向Binder驱动发送请求数据 | IPC.transact() |
BC_REPLY | binder_transaction_data | Server向Binder驱动发送请求数据 | IPC.sendReply() |
BR_PROTOCOL:
响应码 | 参数类型 | 作用 | 触发时机 |
---|---|---|---|
BR_TRANSACTION | binder_transaction_data | Binder驱动向Server端发送请求数据 | 收到BINDER_WORK_TRANSACTION |
BR_REPLY | binder_transaction_data | Binder驱动向Client端发送回复数据 | 收到BINDER_WORK_TRANSACTION |
BR_TRANSACTION_COMPLETE | 无参数 | 对请求发送的成功反馈 | 收到BINDER_WORK_TRANSACTION_COMPLETE |
BC_TRANSACTION和BR_TRANSACTION过程是一个client请求server的完整事务过程
BC_REPLY和BR_REPLY过程是server回复client的完整事务过程。
binder_thread_write()根据不同的BC协议而执行不同的流程。 其中BC_TRANSACTION和BC_REPLY协议,会进入binder_transaction()过程。
BINDER_WORK_TRANSACTION和BINDER_WORK_TRANSACTION_COMPLETE都是binder_work类型。
注:控制协议和驱动协议不止这么点,在此只是列举了最常见的。
四、通信过程
按协议来看:
按数据转换来看:
图(左)说明:
- AMP.startService: 将数据封装到Parcel类型;
- IPC.writeTransactionData:将数据封装到binder_transaction_data结构体;
- IPC.talkWithDriver:将数据进一步封装到binder_write_read结构体;
再通过ioctl()写入命令BINDER_WRITE_READ和binder_write_read结构体到驱动层 - binder_transaction: 将发起端数据拷贝到接收端进程的buffer结构体;
图(右)说明:
- binder_thread_read:根据binder_transaction结构体和binder_buffer结构体数据生成新的binder_transaction_data结构体,写入bwr的write_buffer,传递到用户空间。
- IPC.executeCommand: 解析binder_transaction_data数据,找到目标BBinder并调用其transact()方法;
- AMN.onTransact: 解析Parcel数据,然后调用目标服务的目标方法;
- AMS.startService: 层层封装和拆分后,执行真正的业务逻辑。
五、Binder内存机制
虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间。当Client端与Server端发送数据时,Client(作为数据发送端)先从自己的进程空间把IPC通信数据copy_from_user拷贝到内核空间,而Server端(作为数据接收端)与内核共享数据,不再需要拷贝数据,而是通过内存地址空间的偏移量,即可获悉内存地址,整个过程只发生一次内存拷贝。一般地做法,需要Client端进程空间拷贝到内核空间,再由内核空间拷贝到Server进程空间,会发生两次拷贝。
下面这图是从Binder在进程间数据通信的流程图,从图中更能明了Binder的内存转移关系。
所以总体来说,Binder驱动主要分两块:协议的通信 和 内存空间处理 。
参考文章:
https://blog.csdn.net/qiyei2009/article/details/78996106
http://gityuan.com/2015/11/01/binder-driver/
http://gityuan.com/2015/11/02/binder-driver-2/