binder机制再学习

之前在探索艺术中学习过binder机制以及binder复用池,没有很深的理解,这次跟着大神们的blog再次小小的做一个总结,保证改会的都会。

Binder是Android中IPC机制,也是Android中使用最多的IPC方式,Android是基于Linux的,Linux也有进程间通信的方式比如 管道(pipe)、信号(Signal)、跟踪(Trace)、插口(Socket)、报文队列(message)、信号量(Semaphore)等等。

那为什么使用Binder

基于Client-Server的通信方式已经广泛应用于Android,而之前提到过Linux许多的通信方式只有socket、文件共享、管道和消息队列可以支持Client-Server。但是这些IPC方式效率都很低

消息队列/管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。

而Binder基于Client-Server通信模式,传输过程只需一次拷贝,并且为发送方添加UID/PID身份,既支持实名Binder也支持匿名Binder,保证了Socket(ip地址开放)/SystemV(键值开放)所没有的安全性!

对Server来说,Binder是Server提供特殊服务的访问接入点,对Client来说,Binder是通向Server的管道入口,想要和某个Server通信必须要建立这个管道并获得管道入口。
Binder是一个实体位于Server的对象,这个对象包装了一套方法可以对服务进行请求。遍布于Client中的入口可以看做指向这个binder的指针(没错啦,就是Server的binder在Client中的代理类!或者说叫句柄),调用了这个指针的方法和直接调用Server中Binder的方法没有任何区别。
binder驱动为面向对象编程提供了底层支持。

Binder通信模型

binder有四个主角分别为:Server 、Client (不难理解,因为是基于C/S模式)
ServiceManger 、 Binder驱动。
前三者都作用于用户空间,而Binder驱动作用于内核空间。

这个时候引出用户空间及内核空间的概念,接下来通过一张图来理解这两个概念:
binder机制再学习_第1张图片
看图说话:每个Android进程包含两个空间,一个是内核空间(占用空间少的),特点是不同进程的内核空间是可以共享的,而用户空间的资源是不可以共享的。
我们不同进程要通信的资源肯定都在用户空间啦~所以这个时候就要利用内核空间中的Binder驱动来管理。用户空间通过ioctl等方法来和内核空间通信。

1. Binder驱动
虽然叫“驱动”,但是和硬件驱动没有任何关系,只是作用和驱动差不多。作用于内核态(内核空间),主要负责进程间Binder通信的建立、Binder在进程间的传递、Binder引用计数的管理、数据包在进程间传递和交互的一系列底层支持。

用户态和内核态是隔离开的,但我们用户态也要访问内核态呀,这个时候用户态访问内核态的唯一空间就是系统调用;通过这个统一入口接口,然后所有的资源访问都是在内核的控制下执行,以免导致对用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。在执行内核态的方法是优先级是0(最高)可以使用CPU特殊的优先方法,而用户态则是3(最低)。

Binder 并不是 Linux 内核的一部分,它是怎么做到访问内核空间的呢? Linux 的动态可加载内核模块(Loadable Kernel Module,LKM)机制解决了这个问题;模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行。这样,Android系统可以通过添加一个内核模块运行在内核空间,用户进程之间的通过这个模块作为桥梁,就可以完成通信了。

熟悉了上面的概念可以来看看下图:
binder机制再学习_第2张图片
用户态中的binder_open()、binder_mmap()、binder_ioctl()就是通过调用System call来调用内核态中的方法。用户态和内核态中的共享内存由内核态来调用copy_from_user和copy_to_user来传输。
Binder驱动中保存了一个binder_procs的全局链表来保存服务端的进程信息。

Binder进程和线程
对于底层Binder的驱动,底层的binder_procs链表就记录着所有创建了binder_proc的结构体,每一个binder_proc结构体都与一个用于binder通信的进程对应,每个进程通过单例模式保证自己唯一的ProcessState对象,每个线程也有自己的IPCThreadState对象(也是单例模式保证唯一),在Binder驱动层也有和它对应的Binder_thread,binder_proc结构体中的rb_root threads就是记录自己进程中的所有线程的Binder_thread,具体看下图:
binder机制再学习_第3张图片

2. ServiceManager
ServiceManager是Binder驱动的守护进程,其实作用简化来说就是提供了 查询服务 和 注册服务的功能。
下面介绍ServiceManager主要分成三部分,分别为启动、注册服务、获取服务。

首先是ServceManger的启动,看下图:binder机制再学习_第4张图片

(1)ServiceManager分为framework层和native层,framework层只是对native层进行了封装,方便调用,图上展示的是native层的SM启动过程。
(2)ServiceManager的启动是系统在开机时,init进程解析了init.rc文件调用了 service_manager.c中的main()方法入口启动的 。native层有一个binder.c封装了一些与Binder驱动交互的方法。
(3)ServieceManager启动分为三步:首先打开驱动创建全局链表binder_procs,然后将自己的ProssStats包装成一个binder_proc插入到链表中,最后开启loop循环不断处理共享内存中的数据,并处理BR_xxx命令(ioctl的命令,BR可以理解为binder reply驱动处理完的相应)

其次是ServiceManager的注册
binder机制再学习_第5张图片
(1)比如说我们要注册一个服务端,我们就要通过ServiceManager的addService来实现。
(2)首先ServiceManager向Binder驱动发送BC_TRANSACTION命令(ioctl的命令,BC可以理解为binder client发送过来的请求)携带了ADD_SERVICE_TRANSATION命令,同时注册服务的线程进入等待状态 waitForResponse()。Binder驱动收到请求命令向ServiceManager的todo队列里添加一条事务。接着事务就创建服务进程的binder_porc并插入到链表中。
(3)Binder驱动处理完BR_TRANSATION后会通知ServiceManager,SM接着就像svcinfo中添加已经注册的服务。最后发送BR_REPLY命令唤醒等待的线程,通知注册成功。

ServiceManager获取服务:
binder机制再学习_第6张图片
看的出来其实获取服务和注册服务类似,不过是通过相反的过程。
(1)通过ServiceManager的getService()方法来注册服务。
(2)首先ServiceManager向Binder驱动发送BC_TRANSACTION,命令携带CHECK_SERVICE_TRANSACTION,同时获取服务的线程进入等待态
(3)Binder接到BC_TRANSACTION就会查询binder_procs中是否存在注册的服务,如果存在则立即通过BR_REPLY唤醒等待线程。若查询不到则通过binder_procs中的服务进行一次通讯再响应。

3. 进行一次完整通讯

binder机制再学习_第7张图片
(1)我们在使用Binder时基本调用的都是framework封装好的方法。AIDL就是framework层提供的傻瓜式的使用方法。
(2)如果服务注册好了,我们就通过ServiceManager获取服务端的代理对象BinderProxy,通过调用参数(TRANSATION_test,AIDL中自动生成)传给ServiceManager,同时客户端进入等待状态
(3)ServiceManager将用户空间的参数等请求复制到内核空间,并向服务端插入一条执行事务的方法。事务执行完通知ServiceManager将执行结果从内核空间复制到用户空间。并唤醒等待的线程,相应结果。

上述就是Binder机制的简单介绍。因为并没有涉及到源码部分。

Binder连接池

Binder连接池的主要作用是把Binder请求统一发送至Service执行, 即动态管理Binder操作, 避免重复创建Service.
binder机制再学习_第8张图片
具体实现链接:传送门

你可能感兴趣的:(binder机制再学习)