Binder机制原理简述

参考自大神 https://zhuanlan.zhihu.com/p/35519585
参考自大神 https://blog.csdn.net/carson_ho/article/details/73560642

一 前言
二 Linux传统的进程间通信原理简述
2.1 Liunx 中跨进程通信主要有三个关键信息
2.2 Linux 下的传统 IPC 通信原理
三 Binder 跨进程通信原理
四 Binder 跨进程通信实现
4.1 Binder 跨进程通信实现简述
4.2 Binder 跨进程通信实现步骤
一 前言
因为很多人说 Binder就是跨进程通信方式,但是 Android 又是基于Linux的操作系统,为什么舍弃了Linux已经很成熟的管道、消息队列、共享内存和 Socket 等IPC 机制 而使用所谓的Binder机制呢? 所以在了解 Binder机制之前,我简单了解了一下Linux 的 IPC 机制(Linux传统的进程间通信原理 )作为对比。

二 Linux传统的进程间通信原理简述
在了解 Linux传统的进程间通信原理 之前先了解一下Liunx 中跨进程通信主要有三个关键信息

2.1 Liunx 中跨进程通信主要有三个关键信息:
关键信息1 : 进程隔离
关键信息2 : 进程空间划分:用户空间(User Space)/内核空间(Kernel Space)
关键信息3 : 系统调用:用户态/内核态

关键信息1- 进程隔离 简述:

简单的说就是操作系统中,进程与进程间内存是不共享的。两个进程就像两个平行的世界,A 进程没法直接访问 B 进程的数据,这就是进程隔离的通俗解释。A 进程和 B 进程之间要进行数据交互就得采用特殊的通信机制:进程间通信(IPC)。
关键信息2-进程空间划分 简述:

现在操作系统都是采用的虚拟存储器,对于 32 位系统而言,它的寻址空间(虚拟存储空间)就是 2 的 32 次方,也就是 4GB。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也可以访问底层硬件设备的权限。为了保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间(User Space)和内核空间(Kernel Space)。针对 Linux 操作系统而言,将最高的 1GB 字节供内核使用,称为内核空间;较低的 3GB 字节供各进程使用,称为用户空间。简单的说就是,内核空间(Kernel)是系统内核运行的空间,用户空间(User Space)是用户程序运行的空间。为了保证安全性,它们之间是隔离的。如下图:
在这里插入图片描述

关键信息3-系统调用

虽然从逻辑上进行了用户空间和内核空间的划分,但不可避免的用户空间需要访问内核资源,比如文件操作、访问网络等等。为了突破隔离限制,就需要借助系统调用来实现。系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。Linux 使用级别保护机制:0 级供系统内核使用,3 级供用户程序使用。当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。当进程在执行用户自己的代码的时候,我们称其处于用户运行态(用户态)。此时处理器在特权级最低的(3级)用户代码中运行。系统调用主要通过如下两个函数来实现:

三个关键信息汇总如下图:
在这里插入图片描述

2.2 Linux 下的传统 IPC 通信原理
理解了上面的几个概念,我们再来看看传统的 IPC 方式中,进程之间是如何实现通信的。

通常的做法是消息发送方将要发送的数据存放在内存缓存区中,通过系统调用进入内核态。然后内核程序在内核空间分配内存,开辟一块内核缓存区,调用 copyfromuser() 函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,接收方进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用 copytouser() 函数将数据从内核缓存区拷贝到接收进程的内存缓存区。这样数据发送方进程和数据接收方进程就完成了一次数据传输,我们称完成了一次进程间通信。如下图:
在这里插入图片描述
这种传统的 IPC 通信方式有两个问题:

性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间。
三 Binder 跨进程通信原理
理解了 Linux IPC 相关概念和通信原理,接下来我们正式介绍下 Binder IPC 的原理。

正如前面所说,跨进程通信是需要内核空间做支持的。传统的 IPC 机制如管道、Socket 都是内核的一部分,因此通过内核支持来实现进程间通信自然是没问题的。但是 Binder 并不是 Linux 系统内核的一部分,那怎么办呢?这就得益于 Linux 的动态内核可加载模块(Loadable Kernel Module,LKM)的机制;模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。这样,Android 系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。

在 Android 系统中,这个运行在内核空间,负责各个用户进程通过 Binder 实现通信的内核模块就叫 Binder 驱动(Binder Dirver)。
那么在 Android 系统中用户进程之间是如何通过这个内核模块(Binder 驱动)来实现通信的呢?难道是和前面说的传统 IPC 机制一样,先将数据从发送方进程拷贝到内核缓存区,然后再将数据从内核缓存区拷贝到接收方进程,通过两次拷贝来实现吗?答案是否定的。我们直接对比一下各类IPC的实现方式。
在这里插入图片描述
问题 :发现 Binder 机制 的数据拷贝次数是0,那么 Binder 是怎么实现 0拷贝次数的呢?

这就就要说到 Linux 的另一个概念:内存映射。

Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。
Binder IPC 正是基于内存映射(mmap)来实现的,但是 mmap() 通常是用在有物理介质的文件系统上的。比如进程中的用户区域是不能直接和物理设备打交道的,如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘–>内核空间–>用户空间);通常在这种场景下 mmap() 就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,用内存读写取代I/O读写,提高文件读取效率。而 Binder 并不存在物理介质,因此 Binder 驱动使用 mmap() 并不是为了在物理介质和用户空间之间建立映射,而是用来在内核空间创建数据接收的缓存空间。

一次完整的 Binder IPC 通信过程通常是这样:

步骤1 : 首先 Binder 驱动在内核空间创建一个数据接收缓存区;

步骤2: 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;

步骤3: 发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。

如图:
在这里插入图片描述

四 Binder 跨进程通信实现
4.1 Binder 跨进程通信实现简述
Binder 通信模型是由一系列组件组成的,具体包括:Client、Server、ServiceManager、Binder 驱动四个组件。其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。

Client进程: 使用服务的进程
Server进程: 提供服务的进程
ServiceManager进程:管理 Server 的注册和查询
Binder 驱动 :是连接 Client进程、Server进程、ServiceManager进程之间的桥梁
Client、Server、ServiceManager、Binder 驱动这几个组件在通信过程中扮演的角色就如同互联网中服务器(Server)、客户端(Client)、DNS域名服务器(ServiceManager)以及路由器(Binder 驱动)之前的关系。通常我们访问一个网页的步骤是这样的:

步骤1:首先在浏览器输入一个地址,如 http://www.google.com 然后按下回车键。

步骤2:由于没有办法通过域名地址直接找到我们要访问的服务器,因此需要首先访问 DNS 域名服务器,DNS 域名服务器中保存了 http://www.google.com 对应的 ip 地址 10.249.23.13,所以得到DNS服务器返回的IP地址:10.249.23.13。

步骤3:向IP为 10.249.23.13的服务器发起请求, 才能访问到 http://www.google.com 对应的服务器。

步骤4:应用服务器向客户端返回请求的数据
在这里插入图片描述

4.2 Binder 跨进程通信实现步骤
说到底 Binder 跨进程通信本质来讲就是能够快速的实现进程之间的通讯,本着这一思想,下面列出 Binder 跨进程通信步骤

步骤一:注册Server进程,即注册服务。

步骤1:Server进程 向 Binder驱动 发起服务注册请求
步骤2:Binder驱动 将 Server进程 的注册请求转发给 ServiceManager进程
步骤3:ServiceManager进程 添加该 Server进程,即注册Server服务
结果:此时 ServiceManager进程 拥有了 Server进程 的信息
步骤二:获取服务

步骤1:Client进程 向 Binder驱动 发起获取服务的请求,发送需要获取的服务名称
步骤2:Binder驱动 将该请求转发给 ServiceManager进程
步骤3:ServiceManager进程 查找 Client进程 所需要的 Server进程服务
步骤4:ServiceManager进程 通过 Binder驱动 将上述信息发送给 Client进程
结果:至此 Client进程 与 Server进程 已经建立了链接
步骤三:使用服务

步骤1:Binder驱动为跨进程通信做准备,实现内存映射。即调用 mmap()系统函数

步骤1.1: Binder驱动在内核空间创建 接收缓存区
步骤1.2: Binder驱动实现地址映射关系,即根据 ServiceManager进程中注册表找到 Server进程信息。并且将 内核缓存区 与 Server进程空间地址两处空间地址同时映射到上面创建的 接收缓存区 。此时只是建立了空间映射,但并无数据传输。
步骤2:Client进程 发送数据到 Server进程,即请求服务

步骤 2.1: Client进程 通过系统调用 copy_from_user() 将数据发送拷贝到内核空间的 内核缓存区,拷贝结束后 Client进程被挂起。由于内核缓存区 与 Server进程空间 两个空间 与内核数据缓存区之间存在内存映射,故此时相当于将数据拷贝到了 Server进程空间,即 Binder驱动 实现了 跨进程通讯。
步骤 2.2:Binder驱动 通知 Server进程 执行解包
步骤3:Server进程 根据 Client进程请求,调用目标方法

步骤3.1:Server进程 接收到 Binder驱动通知后,Server进程从线程池中取出线程进行数据解包,并调用目标方法。
步骤3.2:Server进程将最终结果写入映射好的Server进程用户空间
步骤4:Server进程 将目标方法的执行结果返还给 Client进程

步骤4.1:Server进程将最终结果写入映射好的Server进程用户空间,由于内核缓存区 与 Server进程空间 两个空间 与内核数据缓存区之间存在内存映射。故相当于同时写入到了内核缓存区以及数据接收缓存区。
步骤4.2:Binder驱动 通知 Client进程 获取返还结果(此时被挂起的Client进程被唤醒)
步骤4.3: Client进程 通过系统调用:copy_to_user() 从内核缓存区中接收Server进程返回的数据。
过程如图所示:
在这里插入图片描述

注意:

注意1:
Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。其中 Binder驱动 & Service Manager进程 属于 Android基础架构(即系统已经实现好了),而Client 进程 和 Server 进程 属于Android应用层(需要开发者自己实现)。

在这里插入图片描述
注意2:
由于进程隔离,Client、Server 和 ServiceManager 均是通过系统调用 open、mmap 和 ioctl 来访问设备文件 /dev/binder,从而实现与 Binder 驱动的交互来间接的实现跨进程通信。如下图,虚线表示并非直接交互。
在这里插入图片描述

————————————————
原文链接:https://blog.csdn.net/linuxarmbiggod/article/details/86514677

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值