https://blog.csdn.net/ccjhdopc/article/details/50829082
这篇博文写的很好 需要多读几遍,读了两遍才懂了一点点 ,有时间需要再去仔细推敲
下面是从其中再出的一些比较关键的信息!
为什么消息队列和管道的数据传输需要经过2次内存拷贝呢? 首先,数据先从发送方的缓存区(即,Linux中的用户存储空间)拷贝到内核开辟的缓存区(即,Linux中的内核存储空间)中,是第1次拷贝。接着,再从内核缓存区拷贝到接收方的缓存区(也是Linux中的用户存储空间),这是第2次拷贝。
而采用Binder机制的话,则只需要经过1次内存拷贝即可! 即,从发送方的缓存区拷贝到内核的缓存区,而接收方的缓存区与内核的缓存区是映射到同一块物理地址的(理解下这句话就知道为什只有一次了ps只是单向的一次请求包括返回结果的copy),因此只需要1次拷贝即可。
至于共享内存呢,虽然使用它进行IPC通信时进行的内存拷贝次数是0。但是,共享内存操作复杂,也将它排除。
1. Binder实体
Binder实体,是各个Server以及ServiceManager在内核中的存在形式。
Binder实体实际上是内核中binder_node结构体的对象,它的作用是在内核中保存Server和ServiceManager的信息(例如,Binder实体中保存了Server对象在用户空间的地址)。简言之,Binder实体是Server在Binder驱动中的存在形式,内核通过Binder实体可以找到用户空间的Server对象。
在上图中,Server和ServiceManager在Binder驱动中都对应的存在一个Binder实体。
2. Binder引用
说到Binder实体,就不得不说"Binder引用"。所谓Binder引用,实际上是内核中binder_ref结构体的对象,它的作用是在表示"Binder实体"的引用。换句话说,每一个Binder引用都是某一个Binder实体的引用,通过Binder引用可以在内核中找到它对应的Binder实体。
如果将Server看作是Binder实体的话,那么Client就好比Binder引用。Client要和Server通信,它就是通过保存一个Server对象的Binder引用,再通过该Binder引用在内核中找到对应的Binder实体,进而找到Server对象,然后将通信内容发送给Server对象。
Binder实体和Binder引用都是内核(即,Binder驱动)中的数据结构。每一个Server在内核中就表现为一个Binder实体,而每一个Client则表现为一个Binder引用。这样,每个Binder引用都对应一个Binder实体,而每个Binder实体则可以多个Binder引用。
3. 远程服务
Server都是以服务的形式注册到ServiceManager中进行管理的。如果将Server本身看作是"本地服务"的话,那么Client中的"远程服务"就是本地服务的代理。如果你对代理模式比较熟悉的话,就很容易理解了,远程服务就是本地服务的一个代理,通过该远程服务Client就能和Server进行通信
ServiceManager守护进程
ServiceManager是用户空间的一个守护进程。当该应用程序启动时,它会和Binder驱动进行通信,告诉Binder驱动它是服务管理者;对Binder驱动而言,它则会新建ServiceManager对应的Binder实体,并将该Binder实体设为全局变量。为什么要将它设为全局变量呢?这点应该很容易理解--因为Client和Server都需要和ServiceManager进行通信,不将它设为全局变量的话,怎么找到ServiceManager呢!
Server注册到ServiceManager中
Server首先会向Binder驱动发起注册请求,而Binder驱动在收到该请求之后就将该请求转发给ServiceManager进程。但是Binder驱动怎么才能知道该请求是要转发给ServiceManager的呢?这是因为Server在发送请求的时候,会告诉Binder驱动这个请求是交给0号Binder引用对应的进程来进行处理的。而Binder驱动中指定了0号引用是与ServiceManager对应的。
在Binder驱动转发该请求之前,它其实还做了两件很重要的事:(01) 当它知道该请求是由一个Server发送的时候,它会新建该Server对应的Binder实体。 (02) 它在ServiceManager的"保存Binder引用的红黑树"中查找是否存在该Server的Binder引用;找不到的话,就新建该Server对应的Binder引用,并将其添加到"ServiceManager的保存Binder引用的红黑树"中。简言之,Binder驱动会创建Server对应的Binder实体,并在ServiceManager的红黑树中添加该Binder实体的Binder引用。
当ServiceManager收到Binder驱动转发的注册请求之后,它就将该Server的相关信息注册到"Binder引用组成的单链表"中。这里所说的Server相关信息主要包括两部分:Server对应的服务名 + Server对应的Binder实体的一个Binder引用。
Client获取远程服务
Client要和某个Server通信,需要先获取到该Server的远程服务。那么Client是如何获取到Server的远程服务的呢?
Client首先会向Binder驱动发起获取服务的请求。Binder驱动在收到该请求之后也是该请求转发给ServiceManager进程。ServiceManager在收到Binder驱动转发的请求之后,会从"Binder引用组成的单链表"中找到要获取的Server的相关信息。至于ServiceManager是如何从单链表中找到需要的Server的呢?答案是Client发送的请求数据中,会包括它要获取的Server的服务名;而ServiceManager正是根据这个服务名来找到Server的。
接下来,ServiceManager通过Binder驱动将Server信息反馈给Client的。它反馈的信息是Server对应的Binder实体的Binder引用信息。而Client在收到该Server的Binder引用信息之后,就根据该Binder引用信息创建一个Server对应的远程服务。这个远程服务就是Server的代理,Client通过调用该远程服务的接口,就相当于在调用Server的服务接口一样;因为Client调用该Server的远程服务接口时,该远程服务会对应的通过Binder驱动和真正的Server进行交互,从而执行相应的动作。
binder_proc是描述进程上下文信息的,每一个用户空间的进程都对应一个binder_proc结构体。
binder_node是Binder实体对应的结构体,它是Server在Binder驱动中的体现。
binder_ref是Binder引用对应的结构体,它是Client在Binder驱动中的体现。
(01) Binder实体红黑树是保存"binder_proc对应的进程"所包含的Binder实体的,而Binder实体是与Server的服务对应的。可以将Binder实体红黑树理解为Server进程中包行的Server服务的红黑树。
(02) 图中有两棵Binder引用红黑树,这两棵树所包含的Binder引用都是一样的。不同的是,红黑树的排序基准不同,一个是以Binder实体来排序,而另一个则是以Binder引用描述(Binder引用描述实际上就是一个32位的整型数)来排序。以Binder引用描述的红黑树是为了方便进行快速查找。
Binder通信模型