Android Binder 机制学习总结(一)
扫描二维码
随时随地手机看文章
已经不止一次想要花些时间把Android的Binder机制搞搞明白,最近的工作总算比较清闲,所以花了差不多3周时间,看了一些资料,读了不少代码。现在把这些资料整理下,方便自己以后忘了回来看看。
Binder机制的引入原因
Binder机制是为C/S架构设计的IPC机制,基于性能和安全性的考虑,Android系统在传统IPC机制之外,又引入了Binder机制。
性能 传统的Socket/管道/消息队列等IPC机制有一个共同点,数据传输过程中,先从发送方的缓冲区copy到内核缓冲区,再从内核缓冲区copy到接收方缓冲区,数据至少经过两次copy。Binder机制的优化设计,使其仅需要从发送方缓冲区拷贝数据到内核缓冲区,接收方即可直接读取内核缓冲区中的数据,数据仅需一次copy,提升了数据传输性能。安全性 Binder机制提供通信方的UID和PID,有助于判断非法访问。并且,Binder机制支持匿名Binder,可以用于规避通过猜测(i.e. 通过猜测端口地址进行Socket通信)进行通信的风险。Binder机制的角色
Binder机制包括四个角色:Binder driver、Service Manager、Service、Client.
Binder driver
Binder driver工作于内核态(kernel space), 作为linux内核的一部分,跟随linux系统启动。它向linux内核注册了MISC设备,就是我们看到的dev/binder设备文件,当Service/Client调用open/ioctl等系统调用操作dev/binder文件时,就会进入到内核态,执行Binder driver提供的实现(binder_open/binder_ioctl),然后,根据调用者请求的操作(数据写入/发送、数据读取/接收),binder 驱动执行不同的工作。Binder driver没有自己的进程,它总是工作在Client、Service、ServiceManager的进程中。
基于其将一块物理内存同时映射到Binder driver所在的内核空间和接收进程的用户空间地址的设计,当数据从数据发送方的发送缓存中copy到内核缓冲区时,相当于同时也copy到了接收进程的接收缓冲区内,所以,整个数据传输过程中,数据仅需要经过1次copy,提升了数据传输性能。
此外,Binder driver还提供了调用者的UID&PID,这些数据有助于数据接收者判断数据的有效性,回避非法访问,提高系统安全性。
其他方面来说,Binder driver还负责Binder机制使用者(client、service、service manager)的缓存管理,还提供数据接收方线程管理的功能。
Service Manager
Service Manager作为所有实名Binder的管理者,管理着系统中常用的基本Service(这里的Service与Android四大组建之一的Service是不同的概念),例如MediaPlayerService、CameraService、BlueToothService等。
首先,所有这些Service启动后,会把自己注册到Service Manager中。然后,Service Manager就把Service的handle添加到内部的列表中。最后,Client向Service Manager索取Service的Handle时,Service Manager就从内部的列表中查找对应service的handle,并返回给Client,之后Client就可以根据Handle向Service申请自己需要的服务。
从广义角度来说,拥有Binder实体的进程即是Service,从这个角度来看,ServiceManager本身也是一个Service,同时它也是系统内第一个启动的Service。从功能的角度来说,Service Manager相当于一个全局列表,Service把自己添加到列表中,所以Client可以从列表中检索自己需要的Service。
Service
Service即服务的提供者,每个Service都拥有一个Binder实体,Service可以根据需要把自己注册到Service Manager(即实名Binder),也可以不注册(即匿名Binder)。匿名Binder必须依赖实名Binder才能工作(因为它必须通过一个Binder把自己发送到Client端,才能开始Binder通信)。
Client
Client即服务的使用者,Client持有一个Binder引用,而Binder引用则指向特定的Binder实体。Client通过这个Binder引用与Binder实体(即Service)通信,从而获取Service的服务。广义上来说,持有Binder引用即为Binder机制的Client,除了ServiceManager这个特例,ServicerManager持有所有实名Binder的引用,但是从来不呼叫这个Binder的服务。
Binder的工作方式
Binder对象在不同的场景中表现为不同的形式:
Client Service 用户空间 BpBinder (准确的说,应该是继承自BpBinder的对象) BBinder(准确来说,应该是继承自BBinder的对象) 内核空间 binder_ref binder_node 这里的内核空间即binder driver。
模糊的讲,BpBinder和Binder_ref都是Binder引用,而BBinder和Binder_node都是binder实体。
BBinder是服务提供者,它实现了具体的业务逻辑。Binder_node则是Binder driver为BBinder在内核空间创建的对象,BBinder总是和binder_node一一对应,binder_node的ptr成员指针指向BBinder。一个进程可以有多个binder_node,binder_node的ptr成员是“ID”,进程内唯一。Binder_ref则是Binder driver为Client在内核空间创建的对象,binder_ref的node成员指向binder_node,所以一个bind_ref总是关联到一个Binder_node,但是一个binder_node可以被多个binder_ref关联。一个进程可以有多个binder_ref,进程内binder_ref的desc成员是"ID",进程内唯一。BpBinder是BBinder的代理,它实现与BBinder相同的接口,但是不提供具体的服务。BpBinder的handle成员总是和进程内的某个binder_ref的desc成员相同,这样BpBinder才能关联到一个binder_ref,否则BpBinder无法工作。
从上面的图中,我们可以看到client通过BpBinder-》binder_ref-》binder_node-》BBinder最终关联到一个Service,所以,Client可以通过BpBinder向Service发起请求。而Service无法主动联系Client,因为同时可以有多个Client关联到Service。
整个Binder机制的工作过程是这样的(暂不讨论匿名Binder):
Linux系统启动,Binder driver开始工作,注册设备文件/dev/binderAndroid系统启动,ServiceManager开始工作,向Binder driver注册ContextManager,这个过程中,Binder driver中创建了第一个binder_node(注意:ServiceManager在内核空间有binder_node,但是在用户空间没有对应的BBinder)。Service进程启动,在用户空间创建了BBinder,并向ServiceManager注册服务,注册的过程中,Binder driver为Service在内核空间创建了binder_node
Client启动,向ServiceManager请求指定Serivce的Handle,这个过程中,Binder driver为Client在内核空间创建了handle对应的binder_ref
Client根据ServiceManager提供的handle,向Service请求服务。
而上面第5步中,一个“完整”的Binder通信过程(Client发起请求》》Service读取请求》》Service处理请求》》Service回复处理结果》》Client端读取结果),大致流程如下:
Client进程在用户态调用BpBinder的接口BpBinder调用ioctl向dev/binder文件写入数据,数据中包含自己的handle进程进入到核心态,执行binder driver的代码,先查找handle关联的binder_ref进一步根据binder_ref关联的binder_node,确定目标Service进程即(binder_proc)把数据保存到目标进程(或目标线程)的todo队列,这时的数据中添加了当前线程(binder_thread)信息,并唤醒ServiceClient开始等待回复Service进程内的binder driver被唤醒,缓存client发送过来的数据Service进程返回用户态,调用BBinder到接口,开始处理请求请求处理结束,调用ioctl,回复Client的请求进程进入核心态,通过步骤7缓存的数据,确定请求发起线程(步骤五中,数据内添加了请求发起线程的信息)把回复数据保存到client进程请求线程的todo队列中,并唤醒Client进程中的请求线程
Service进程继续等待请求Client进程内的请求线程被唤醒,返回用户态返回到用户态,client进程处理回复数据
(这个时序图中,BBinder和Service被画在一起,因为Android中很多Service的直接继承自BBinder,所以Service和BBinder可以是一体的)
个人对于Binder机制的理解
作为一个为C/S架构设计的IPC机制,Binder机制对于C/S架构有相当良好的支持:
Service可以同时为多个Client提供服务。Client可以同时呼叫多个Service的服务。Service和Client的角色并不互斥,同一个进程可以同时身兼两个角色(i.e. MediaPlayerService 在向MediaPlayer提供服务担当着Service的角色,但是向ServiceManager注册服务的过程中,则扮演着Client的角色)。
Binder通信支持同步通信,也支持异步通信,但是异步通信的处理优先级低于同步通信。
但是Binder机制也存在缺陷,成然Binder机制支持双工通信,但是不同与管道和Socket,通信只能由Client发起,然后Service应答(这点和HTTP协议类似)。Service无法主动向任何一个Client发起通信。当然,这个问题也并不是无法解决,通信双方同时担任Sevice和Client的角色就可以解决问题(i.e. ActivityManagerService启动新的activity时,ActivityManagerService和ActivityThread就是这样的情况: ActivityManagerService通过ApplicationThreadProxy控制ApplicationThread创建Activity,而ApplicationThread则通过ActivityManagerProxy通知ActivityManagerService操作结果。)
参考资料:
Android进程间通信(IPC)机制Binder简要介绍和学习计划
Android深入浅出之Binder机制
Android Binder设计与实现 – 设计篇