Android 进程间通信(二) -- 理解 Binder 的机制

参考 写给 Android 应用工程师的 Binder 原理剖析 一些文字和图片均参考该文

系列文章
Android 进程间通信(一) – Android 多进程模式
Android 进程间通信(二) – 理解 Binder 的机制
Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务

上一章中,已经理解了进程之间通信的一些基本知识,这一章来好好学习 Binder。

一、为啥使用Binder

我们知道,Android 底层使用了大量的 Binder 来进行进程之间的通信。那为啥要新设计个 Binder ,而不是用传统的IPC 通信方式呢?
主要是考虑到以下几个方面:

  1. 性能方面:Socket 作为通过接口,但传输效率低,开销大,且阻塞 IO,一般用于跨网络的进程间通信;而消息队列和管道,则采用用存储-转阿发方法,至少经过两次拷贝;共享内存虽然无需拷贝,但实现复杂,控制也麻烦。

图片原来于 Android Binder 设计与实现在这里插入图片描述

  1. 稳定性:Binder 基于C/S架构,client 有什么需求就丢给 server,架构清晰,又相互独立。
  2. 安全性:Android 为每个应用都分配了自己的UID,用来鉴别身份,而传统的 IPC 则无法做到。

想要了解 Binder ,想了解 传统的 Linux 的IPC 机制。

1.1、linux 的 IPC 机制

Linux 采用了虚拟地址空间地址,操作系统将虚拟内存分为 用户空间 (User space) 和内核空间 (Kernel) ,普通的应用程序运行在用户空间,而系统内核运行在内核空间;这也是我们常说的两进程之间的数据不共享,不通过特殊手段不共享的问题。

在这里插入图片描述
从上图可以看出来传统的Linux跨进程涉及的一些点。

  • 进程隔离
  • 进程空间划分:用户空间(User space) 和 内核空间(Kerner space)
  • 系统调用状态:内核态和用户态

1.1.1 进程隔离

在操作系统中,两个进程之间的数据时不共享的,必须通过特殊的通信机制,进程间数据才能共享。

1.1.2 进程空间划分

现在的操作系统都是采用虚拟存储器,对于 32 位系统而言,它的寻址地址就是 2的32次方,即 4GB;
对操作系统而言,其中核心的部分称为内核,它的权限最高,可以访问受限的内存空间,也可以访问硬件设备,为了保护用户进程不能直接操作内核,保证内核的安全性,从逻辑上,将虚拟空间分户为用户空间和内核空间。
其中将高地址的1G划分为内核空间,而低地址的3G划分为用户空间;
在这里插入图片描述

1.1.3 系统调用: 内核态,用户态

虽然有以上哪些划分,但两进程之间不可能永远不通信;当两进程需要进行数据交互时,就需要系统调用来实现。系统调用时用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核控制下进行了,避免了越权访问,提供系统稳定性。

Linux 使用两级保护机制:0 级供系统内核使用,3 级供用户程序使用。
当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。

当进程在执行用户自己的代码的时候,我们称其处于用户运行态(用户态)。此时处理器在特权级最低的(3级)用户代码中运行。

系统调用如下两个函数来实现:

copy_from_user() //将数据从用户空间拷贝到内核空间
copy_to_user() //将数据从内核空间拷贝到用户空间

一般来说,A进程和B进程是无法和对方数据交互,但有些情况下,就需要两个进程之间有交互,而这个交互过程就叫做 IPC (Inter Process Communication ,进程间通信),IPC 的实质是 数据的交互。IPC 的通信过程如下:

  • A进程发送方,把要发送的数据放到 用户空间的内存缓存区
  • 内核程序在内核空间开辟一块内核缓存区,并将A进程用户空间的数据,通过 copy_from_user 从内存缓冲区,拷贝到内核空间的内核缓冲区。
  • B进程接收方,也在用户空间开辟一块 内存缓存区,准备接受数据
  • 内核程序将内核缓存区通过 copy_to_user 将数据拷贝到用户空间的内存缓存区。

在这里插入图片描述
通过以上过程,IPC 一次就完成了,但这种方式有比较大的缺陷:

  • 由于不知道需要多大内存用于存放数据,因此都是尽可能开辟大的内存,会导致浪费
  • 性能较低,整个过程需要经过A进程 内存缓存区 - 内核缓存区 - 内存缓存区,需要两次拷贝。

1.2 Binder IPC通信原理

为了克服传统 IPC 的不足;Android 引入了 Binder 机制。Binder 在数据交互这块,可以充当是一个桥梁的作用,让两个进程之间能够相互通信。

从上面知道,数据的通信少不了内核的帮助,而Binder不属于内核,但通过 Linex 的 LKM 机制:

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

因此,Binder 作为模块存在于内核中,即成为 Binder 驱动。回顾上一节,传统IPC进程之间的通信需要两次的数据拷贝,Binder 却可以借助 Linux 的另一个特性,只用一次性拷贝,就能实现 IPC 过程,这个就是内存映射

Binder IPC 涉及到的内存映射通过 mmap() 来实现,mmap()是操作系统中一个内存映射的方法;简单来说,内存映射,就是把 用户空间的一块内存区域映射到内核空间,映射建立之后,用户空间的数据,就能反映到这块内核空间的内存来,这样,当用户空间的数据修改,内核空间的数据也会跟着被改动

内存映射能减少拷贝次数,实现用户空间和内核空间的效率互动。两个空间各自修改的数据能直接反射在映射的内存区域,从而被对方空间及时感知。

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

  1. Binder 驱动在内核空间建立内核缓存区
  2. Binder 驱动在内核空间创建接收数据缓存区,并与内核缓存区建立映射,以及与接收进程的 用户空间地址建立映射关系
  3. 发送方进程通过系统调用 copy_from_user() 将数据copy 到内核中的内核缓存区,由于内核缓存区与接收数据缓存区有映射,接收数据缓存区又与接收方的用户空间地址有映射,所以,数据直接就到接收方的用户空间上了。
    如下图:

在这里插入图片描述

二. Binder 通信模型

上面介绍了 Binder IPC 的通信原理,这里实现层是如何设计的。

上面说到,Binder 是基于 C/S 架构的,由一系列组件组成,包括 Client 、Server、ServerManager,Binder驱动等

其中 Client 、Server、ServerManager 运行在用户空间,Binder 驱动运行在内核空间。ServerManager 和 Binder 驱动是由系统提供,而 Client 、Server则由用户自己创建。

Client 、Server、ServerManager,均是通过系统调用 open、mmap 和ioctl 来访问设备文件 /dev/binder 的,从而实现与 Binder 的交互来 间接实现进程之间的数据通信。
如下图:
在这里插入图片描述

  • Binder 驱动,已经解释过了,就是两个两个进程之间的桥梁
  • ServiceManager :它是binder的服务大管家,它的作用只有一个,注册和查询。一个Binder注册的时候,会携带对应的字符串,而 client 在获取这个binder 的时候,就可以通过这个字符串,通过 ServiceManager 查询拿到 binder。它也是一个looper循环。

三. Binder 的代理模式

从上面已经清楚,Client、Server 借助Binder驱动,完成跨进程的实现机制。
但有个问题,A 进程想要获取B进程的 object是,驱动是不是真的就把 object 返回 A ?

当然不是,当数据在 Binder 驱动 流过时,会对数据进行一层转换;当 A 想获取 B 的object 对象时,驱动并不会把 object 对象给 A,而是返回了一个跟 object 一模一样的代理对象 objectProxy,objectProxy 不具备 object 方法的能力;当A 使用 objectProxy 时,只需要把参数通过 objectProxy 给 Binder 驱动就可以了,看起来就像调用了 object 了。

当B接收到 A 进程的消息时,发现这个是 objectProxy,就去查询自己的表达那,一旦发现这个 B 进程的object 代理对象,就会通知 B 调用 object方法,并把结果返回给自己 或者 A 进程。如下:
在这里插入图片描述
这个章节,在 AIDL 的时候再来详细分析。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android进程通信(Inter-process Communication, IPC)是指在不同进程进行数据交互和通信的方法。Android提供了多种方式实现进程通信,以下是常用的几种方式: 1. Binder机制Binder是一种跨进程通信技术,它基于Linux内核提供的Binder驱动。通过Binder,我们可以将一个Service注册为Binder服务,其他进程可以通过Binder进行远程调用,实现进程通信。 2. 文件共享:进程可以通过共享文件的方式实现通信。一个进程将数据写入文件,其他进程读取该文件数据,从而实现进程的信息传递。 3. Socket通信:可以使用Socket套接字进行进程通信。一个进程作为服务器,另一个进程作为客户端,通过Socket建立连接进行数据交互。 4. ContentProvider:ContentProvider是Android中用于实现进程共享数据的一种组件。通过ContentProvider,一个进程可以提供数据给其他进程进行读写操作。 5. BroadcastReceiver:广播是一种常见的进程通信方式。一个进程发送广播消息,其他进程通过注册相应的广播接收器来接收并处理广播消息。 6. Messenger:Messenger是一种轻量级的进程通信方式。通过Messenger,一个进程可以发送消息给另一个进程,实现进程通信。 以上是常用的几种Android进程通信方式,开发者可以根据具体需求选择合适的方式来实现进程通信

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值