Binde机制——原理

原理

Android 系统 是基于Linux 系统 ,因此想要深入了解Binder 机制。那么我们需要了解一部分Linux 知识。

Linux预备知识

  1. 进程隔离
  • 简单的说就是操作系统中,进程与进程间内存是不共享的。两个进程就像两个平行的世界,A 进程没法直接访问 B 进程的数据,这就是进程隔离的通俗解释。A 进程和 B 进程之间要进行数据交互就得采用特殊的通信机制:进程间通信(IPC)。
  1. 用户空间/内核空间
  • 现在操作系统都是采用的虚拟存储器,对于 32 位系统而言,它的寻址空间(虚拟存储空间)就是 2 的 32 次方,也就是 4GB。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也可以访问底层硬件设备的权限。为了保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间(User Space)和内核空间(Kernel Space)。针对 Linux 操作系统而言,将最高的 1GB 字节供内核使用,称为内核空间;较低的 3GB 字节供各进程使用,称为用户空间。
  • 内核空间只有一个,用户空间可以有多个。
  • 如果用户空间想要操作,内核空间必须进行系统调用,而且这些操作必须得到内核的允许和支持。传统的Linux 系统调用:是消息发送方将要发送的数据存放在内存缓存区中,通过系统调用进入内核态。然后内核程序在内核空间分配内存,开辟一块内核缓存区,调用 copy_from_user() 函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,接收方进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用 copy_to_user() 函数将数据从内核缓存区拷贝到接收进程的内存缓存区
copy_from_user() //将数据从用户空间拷贝到内核空间
copy_to_user() //将数据从内核空间拷贝到用户空间
  1. 系统调用/内核态/用户态

虽然从逻辑上进行了用户空间和内核空间的划分,但不可避免的用户空间需要访问内核资源,比如文件操作、访问网络等等。为了突破隔离限制,就需要借助系统调用来实现。系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。

  • 内核运行态(内核态): 当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。
  • 用户运行态(用户态):当进程在执行用户自己的代码的时候,我们称其处于用户运行态(用户态)。此时处理器在特权级最低的(3级)用户代码中运行。

4.驱动

  • 驱动程序一般指的是设备驱动程序(Device Driver),是一种可以使计算机和设备通信的特殊程序。相当于硬件的接口,操作系统只有通过这个接口,才能控制硬件设备的工作。
  • 例如:我们window系统需要操作鼠标键盘,因此我们会安装鼠标和键盘的驱动,来使这两个外设,与操作系统进行通讯。
  1. 模块

模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。eg:Binder ,各种驱动

  1. Linux 动态加载模块机制(LKM)

简单点理解,就是内核在运行期间,可以动态加载其它以前没有的模块,扩展功能。

Binder产生

从第一天接触Binder,我们就知道,Binder 是Android 系统特有的IPC 机制 。那么问题来了:

  1. 为什么Binder是 Android 系统特有的?而Linux系统没有这种机制?
  2. 为什么Linux 有那么多传统的IPC,Android系统没有沿用 ?

传统的IPC(管道,System V IPC,即消息队列/共享内存/信号,socket),他们都是内核的一部分,因此内核一定支持。而Binder 并不属于内核固有的东西,它是作为一种驱动模块,在Android系统运行期,被动态加载进来的模块。so,Binder 是Android系统独有的(相对于Linux系统)。

Android 采用Binder的原因:

1.效率:

  • 传统的IPC(管道,消息队列,socket 等),每次都需要从 发送方缓存区 copy_from_user() 到 内核缓存区,然后通过从内核缓存区,通过copy_to_user()到 接收方缓存区,至少经过2次拷贝,效率很低。
  • 传统的IPC(共享内存),虽然无拷贝,但是难以使用。
  • 而Binder 使用的是“内存映射技术”,只需要一次拷贝。
内存映射:

内存映射
1). Binder 驱动 会在内核空间中,建立每个用户空间虚拟内存缓冲区 映射的 数据接收缓存区,接着会在内核中建立一个 内核缓存区。并在内核中建立了,每个数据接收缓冲区 和 内核缓存区的映射。也相当于,用户空间的虚拟内存缓存区之间间接存在着一种映射关系。
2). 发送方,只需将用户空间的数据,发送到自己虚拟内存空间,那么就会直接同步到 接受进程的虚拟内存缓冲区。可见效率之高。

2. 稳定:
Binder 采用C/S架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好。共享内存虽然无需拷贝,但是控制负责,难以使用。从稳定性的角度讲,Binder 机制是优于内存共享的。

3. 安全:
1. 传统的协议,只能靠上层的协议确保安全,容易被劫持。而Binder机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性。这个也是Android权限模型的基础。

Binder 通信模型

在这里插入图片描述

  1. Client/Server/ServiceManager/驱动

Binder 驱动是基于C/S架构,它的通信流程包括了, server ,Client,ServerManager,Binder 驱动。Binder 驱动是系统运行时,动态链接的,它运行在内核中。ServerMangaer 是由系统创建,它运行在用户空间的独立进程中。所以,对于开发者来说,我们只要关心,Server 和 Client 的创建

Binder 驱动
Binder 驱动就如同路由器一样,是整个通信的核心;驱动负责进程之间 Binder 通信的建立,Binder 在进程之间的传递,Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

ServiceManager 与实名 Binder
ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了除了有 IP 地址意外还有自己的网址。Server 创建了 Binder,并为它起一个字符形式,可读易记得名字,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“张三”的 Binder,它位于某个 Server 中。驱动为这个穿越进程边界的 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。ServiceManger 收到数据后从中取出名字和引用填入查找表。
细心的读者可能会发现,ServierManager 是一个进程,Server 是另一个进程,Server 向 ServiceManager 中注册 Binder 必然涉及到进程间通信。当前实现进程间通信又要用到进程间通信,这就好像蛋可以孵出鸡的前提却是要先找只鸡下蛋!Binder 的实现比较巧妙,就是预先创造一只鸡来下蛋。ServiceManager 和其他进程同样采用 Bidner 通信,ServiceManager 是 Server 端,有自己的 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 实体现在有两个引用:一个位于 ServiceManager 中,一个位于发起请求的 Client 中。如果接下来有更多的 Client 请求该 Binder,系统中就会有更多的引用指向该 Binder ,就像 Java 中一个对象有多个引用一样。

  1. 通信过程

    • Server 端的 Binder,通过Binder驱动帮助,将自己的标识字符串和 Binder引用,注册到了ServerManager中(当然这个注册过程,也是通过Binder进行的IPC 过程,只是系统帮我们做了前提工作)。ServerManger 中,维护着一个表,存储着,各种Binder引用和 代表它的字符。
    • Client 通过 ,需要的Binder 对应字符串,经过Binder驱动的帮助,向ServerManager 发出指令。ServerManager,通过这个字符串,查询自己维持的Binder表,查找到对应的Binder引用,又经过Binder驱动,返回给了,查询的Client。此时,Client ,获得了Server 中的Binder 的引用,即Binder的代理类。
    • Client 通过,查询得到到的Binder代理类,开始发送数据,经过 Binder驱动的再一次转换,发送到了Server Binder的具体方法中
  2. 回看Binder (Binder 到底是什么?)

  • 从进程间通信的角度看,Binder 是一种进程间通信的机制;
  • 从 Server 进程的角度看,Binder 指的是 Server 中的 Binder 实体对象;
  • 从 Client 进程的角度看,Binder 指的是对 Binder 代理对象,是 Binder 实体对象的一个远程代理
  • 从传输过程的角度看,Binder 是一个可以跨进程传输的对象;Binder 驱动会对这个跨越进程边界的对象对一点点特殊处理,自动完成代理对象和本地对象之间的转换

参考

Android Bander设计与实现 - 设计篇
Binder 学习指南

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值