1、Binder的通信原理
动态内核可加载模块&&内存映射
在Android Binder开卷中所说的IPC通信模型中所描述的,跨进程通信需要内核空间做支持。传统的IPC机制如管道,Socket都是内核的一部分,因此通过内核支持来实现进程间通信自然没有问题。但是Binder并不是Liunx系统内核的一部分。Binder机制得益于Liunx的动态内核可加载模块(Loadable Kernel Module,LKM)的机制,模块是具有独立功能的程序,它可以单独编译,但是不能独立运行,它运行时被链接到内核作为内核的一部分运行。这样,Android系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。
在Android系统中, 驱动(Binder Dirver)。
Binder是基于内存映射来实现的,在Android Binder开卷中又分析到内存映射通常是用在有物理介质的文件系统上的,Binder没有物理介质,它使用内存映射是为了跨进程传递数据。
Binder通信的步骤:
- Binder驱动在内核空间创建一个数据接收缓存区;
- 在内核空间开辟一块内核缓存区,建立内核缓存区和数据接收缓存区之间的映射关系,以及数据接收缓存区和接收进程用户地址的映射关系;
- 发送方进程通过copy_from_user()函数将数据拷贝到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成一次通信;
2、Binder通信模型
Binder通信采用C/S架构
- Client 、Server、Service Manager运行在用户空间,Binder驱动运行在内核空间。
- Client、Server由应用程序来实现
- Server Manager和Binder驱动由系统实现
- Client、Server和ServerManager均是通过系统调用open、mmap和ioctl来访问设备文件/dev/binder,从而实现与Binder驱动的交互来间接实现跨进程通信。
- ServiceManager进程是管理Service注册与查询(将字符形式的Binder名字转化成Client中对该Binder的引用)。
- Binder驱动是一种虚拟设备驱动,是连接Service进程、Client进程和Service Manager的桥梁,具体有两个功能:1、传递进程间的数据(通过内存映射)2、实现线程控制:采用Binder的线程池,并由Binder驱动自身进行管理。Binder驱动就如同路由器一样,是整个通信的核心,负责进程间Binder通信的建立。
通常我们访问一个网页的步骤是这样的:首先在浏览器输入一个地址,如www.google.com然后按下回车键。但是并没有办法通过域名地址直接找到我们要访问的服务器,因此需要首先访问DNS域名服务器,域名服务器中保存l了www.google.com对应的ip地址10.249.23.13,然后通过这个ip地址才能放到www.google.com对应的服务器。
ServiceManager与实名Binder
ServiceManager和DNS类似,作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder的名字获得对Binder实体的引用。注册了名字的Binder叫实名Binder,就像网站一样除了有IP地址以外还有自己的网址。Server创建了Binder,并为它起一个字符形式,可读易记的名字,将这个Binder实体连同名字一起以数据包的形式通过Binder驱动发给ServiceManager,通知ServiceManager注册一个名为“张三”的Binder,它位于某个Server中。驱动为这个穿越边界的Binder创建位于内核中的实体节点以及ServiceManager对实体的引用,将名字以及新建的引用打包传给ServiceManager。ServiceManager收到数据后从中取出名字和引用填入查找表。ServiceManager是一个进程,Service是另外一个进程,Service向ServiceManager中注册Binder必然涉及到进程通信。当前实现进程又要用到进程间通信,那么第一个进程来自于哪里?ServiceManager和其它进程同样采用Binder通信,ServiceManager是Service端,有自己的BInder实体,其它进程都是Client,需要通过这个Binder的引用来实现Binder的注册、查询和获取。ServiceManager提供的Binder比较特殊,它没有名字也不需要注册。当一个进程使用BINDER_SET_CONTEXT_MGR命令将自己注册成ServiceManager时,Binder驱动会自动为它创建Binder实体(这就是预先生成的)。其次这个Binder实体的引用在所有Client中都固定为0而无需通过其它手段获得。也就是说,一个Server想要向ServiceManager注册自己的Binder就必须通过这个0号引用和ServiceManager的Binder通信。类比互联网,0号引用就好比是域名服务器的地址,你必须预先动态或者手动配置好。需要注意的是,这里说的Client是相对于ServiceManager而言的,一个进程或者应用程序可能是提供服务的Server,但是对于ServiceManager来说它任然是个Client。
Client获取Binder的引用
Server向ServiceManager中注册了Binder以后,Client就能通过名字获得Binder的引用了。Client也利用保留的0号引用向ServiceManager请求访问某个Binder:我申请访问名字叫张三的Binder引用。ServiceManager收到这个请求后从请求数据包中取出Binder名称,在查找表里找到对应的条目,取出对应的Binder引用作为回复发送给发起情求的Client。从面向对象的角度看,Server中的Binder实体现在有两个引用:一个位于ServerManager中,一个位于发起请求的Client中。如果接下来有更多的Client请求该Binder,系统中就会有更多的引用指向该Binder,就像java中一个对象有多个引用一样。
Binder通信过程
Binder通信过程如下:
1、首先,一个进程使用BINDER_SET_CONTEXT_MGR 命令通过Binder驱动将自己注册成为ServiceManager;
2、Server通过驱动向ServiceManager中注册Binder(Server中的Binder实体),表明可以对外提供服务。驱动为这个Binder创建位于内核中的实体节点以及ServiceManager对实体的引用,将名字以及新建的引用打包传给ServiceManager,ServiceManager将其填入查找表。
3、Client通过名字,在Binder驱动的帮助下从ServiceManager中获取到对Binder实体的引入,通过这个引用就能实现和Server进程的通信。
Binder通信中的代理模式
A进程想要B进程中某个对象(object)是如何实现的呢?毕竟它们分属不同的进程,A进程没法直接使用B进程中的object。
数据流经Binder驱动的时候,驱动会对数据做一层转换。当A进程想要获取B进程中的object时,驱动并不会真的把object返回给A,而是返回了一个跟object看起来一模一样的代理对象objectProxy,这个objectProxy具有和object一某一样的方法,但是这些方法并没有B进程中object对象那些方法的能力,这些方法只需要把请求参数交给驱动即可。这对于A进程来说和直接调用object中的方法时一样的。
当Binder驱动接收到A进程的消息后,发现这个是objectProxy就去查询直接维护的表单,一查发现这是B进程object的代理对象。于是就会去通知B进程调用object的方法,并要求B进程把返回结果发给自己。当驱动拿到B进的返回结果后就会转发给A进程,一次通信就完成了。
、
来源:CSDN
作者:一个小菜鸟的成长之路
链接:https://blog.csdn.net/u011164827/article/details/103691308