Android之Binder通信篇
Binder跨进程通信的本质是依赖内核驱动将属于不同Binder进程的数据,从原始进程复制到⽬标进程,这样就完成了跨进程通信了。
好了就这些,简单吧?
逗我玩
全国⼈民都爱的⽑爷爷说过⼀句⾄理名⾔:我们要从战略上藐视敌⼈,从战术上重视敌⼈!虽然Binder跨进程通信⽅式跟Linux系统其它跨进程通信⽅式⼀样,都是把数据从⼀个进程发送到另⼀个进程,但是在具体的实现⽅式必须独树⼀帜!Binder确实做到了。
⼀、传输效率⾼
进程通信方式Binder通过独特的内存映射机制,在跨进程通信时,可以做到⼀次拷贝,两个空间同时使⽤!如下图:
图1.内存拷贝图
进程A向进程B传递数据时,Binder驱动通过⼀次拷贝,将进程A⽤户空间的数据拷贝到进程B内核空间的共享内存中。由于Binder进程的内存分配机制是,每⼀个Binder进程中内核空间和⽤户空间针对同⼀段物理内存建⽴映射,那么你懂的!放在同⼀块物理地址中的数据⾃然可以被内核空间和⽤户空间同时访问。但是⾯试官可能会问你内核空间和⽤户空间的内存地址是否⼀样,告诉他不⼀样,两者之间存在⼀个固定的差值。
⾄于内核空间和⽤户空间是Linux内核地址空间的内存划分的范畴,有些专家岗对这个颇在乎,我会专门学习~
对了,关于进程B的拷贝响应数据到进程A的过程,也是这种机制。值得注意的是,Binder驱动要单独申请物理内存完成映射。试想,如果两次拷贝使⽤相同的物理内存地址,就跟shared memory共享内存这种跨进程通信⽅式完全⼀样了,也就⽆法避免共享内存借助于其他通信⽅式完成多
进程同步的弊端。
Binder跨进程通信的效率是相对的,⽐管道/消息队列等IPC效率⾼,因为管道/消息队列不但要复制到内核空间还要复制到⽤户空间。⽐共享内存安全性好,不必考虑进程间同步问题。
⼆、虚拟硬件映射内存mmap
Binder跨进程通信是要传递数据的,既然有数据必然要占⽤内存空间,Android系统规定每⼀个进程都有⼀块Binder内存区,也就是图1中的共享内存,系统最多只能给该区域分配4M的物理内存,由于申请这块内存是通过系统的mmap函数完成的,所以整个映射机制⼜被称为mmap机制为了把这部分说明⽩,就再盗图⼀张,命名图2吧!
图2.内存映射机制
mmap机制是Linux系统为具有物理存储介质的⽂件系统提供的映射函数,简⾔之,它服务于内核空间,⽽内核空间是可以跨进程通信的。Android系统为了使⽤这种机制,通过Linux 的动态可加载内核模块(Loadable Kernel Module,LKM)机制,LKM在运⾏时被链接到内核作为内核的⼀部分在内核空间运⾏。这样,Android系统可以通过LKM添加⼀个内核模块运⾏在内核空间,⽤户进程之间的通过这个模块作为桥梁,完成通信。在 Android 系统中,这个运⾏在内核空间的,负责各个⽤户进程通过 Binder 通信的内核模块叫做 Binder 驱动。驱动就是操作硬件的接⼝,为了⽀持Binder通信过程,Binder 成为了⼀种“硬件”,因此这个模块被称之为Binder驱动。
图2中通信时,先调⽤binder_open函数打开Binder驱动,然后通过binder_mmap申请⼀块共享内存,然后将这块共享内存映射到接收进程的内核空间和⽤户空间。每当发送进程向接收进程传递数据时,Binder驱动程序就在共享内存中根据最优算法申请⼀块区域,并将数据从发送进程对应的共享内存拷贝过来。
但是Binder通信采⽤的消息+数据的格式传递信息,仅仅将数据拷贝过来拿到映射是没有⽤的,消息结构体如何调度呢?
Linux内核并没有从⼀个⽤户空间到另⼀个⽤户空间直接拷贝的函数,⽽是先⽤copy_from_user()拷贝到内核空间,再⽤copy_to_user()拷贝到另⼀个⽤户空间。这样⽤户空间就可以通过mmap映射读取内核空
间存储的数据了。Binder驱动承担了有效负荷的分配和释放任务,由于采⽤mmap机制提升了⼀倍性能,消息头结构体还是要接收⽅来存储的,但是其⼤⼩可控,属于辅助。
三、Binder进程和线程
Binder进程和线程
对于Binder驱动,通过 binder_procs 链表记录所有创建的 binder_proc 结构体,binder 驱动层的每⼀个 binder_proc 结构体都与⽤户空间的⼀个⽤于 binder 通信的进程⼀⼀对应,且每个进程有且只有⼀个 ProcessState 对象,这是通过单例模式来保证的。在每个进程中可以有很多个线程,每个线程对应⼀个 IPCThreadState 对象,IPCThreadState 对象也是单例模式,即⼀个线程对应⼀个 IPCThreadState 对
象,在Binder 驱动层也有与之相对应的结构,那就是 Binder_thread 结构体。在 binder_proc 结构体中通过成员变量 rb_root threads,来记录当前进程内所有的 binder_thread。
Binder 线程池:每个 Server 进程在启动时创建⼀个 binder 线程池,并向其中注册⼀个 Binder 线程;之后 Server 进程也可以向 binder 线程池注册新的线程,或者 Binder 驱动在探测到没有空闲 binder 线程时主动向 Server 进程注册新的的 binder 线程。对于⼀个 Server 进程有⼀个最⼤ Binder 线程数限制,默认为16个 binder 线程,例如 Android 的 system_server 进程就存在16个线程。对于所有 Client 端进程的binder 请求都是交由 Server 端进程的 binder 线程来处理的。
四、参考博客
1. Android Bander设计与实现 - 设计篇
2. ⼀篇⽂章了解相见恨晚的 Android Binder 进程间通讯机制

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。