Binder机制详解(二)

本文详细介绍了MMU(Memory Management Unit)的作用和历史,阐述了虚拟内存和物理内存的关系,以及分页机制的概念。虚拟地址通过页表映射到物理地址,实现了内存管理和程序运行的效率。页的概念、页表以及页式内存管理中的页表条目(PTE)是关键点。内容涵盖了虚拟内存如何通过页和页表在物理内存和磁盘间动态交换,以支持大内存需求的进程在有限物理内存中运行。

系列章节:

Binder机制详解(一)

Binder机制详解(三)


前言

上一章通过一个例子让我们认识了Binder通信机制不同于Linux的地方,还了解了用户空间和内核空间,这章我们主要来讲解MMU内存管理单元


一、什么是MMU

MMU(Memory Management Unit)主要用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权、多任务多进程操作系统。

二、发展历史

注意:学习一个知识点
很重要的一步是了解其为什么而存在?
它的存在是为了解决什么问题?
最后,在学习的过程中带着这些问题去理解、去思考

在许多年以前,还是使用DOS或一些古老的操作系统时,内存很小,同时,应用程序也很小,将程序存储在内存中基本能够满足需要。随着科技的发展,图形界面及一些其他更复杂的应用出现,内存已经无法存储这些应用程序了,通常的解决办法是将程序分割成很多个覆盖块,覆盖块0最先运行,运行结束之后,就调用另一个覆盖块,虽然这些操作由OS来完成,

​ 但是,需要程序员对程序进行分割,这非常不高效;因此,人们想出了一个**虚拟存储器(virtual memory)**的方法。

​ 虚拟存储器的基本思想是:程序、数据、堆栈的总大小可以超过内存空间的大小,

​ 操作系统将当前运行的部分保存在内存中,未使用的部分保存在磁盘中。

​ 比如一个16MB的程序和一个内存只有4MB的机器,操作系统通过选择可以决定哪部分4MB的程序内容保存在内存中,并在需要时,在内存与磁盘中交换程序代码,

这样16MB的代码就可以运行在4MB的机器中了

三、相关概念

地址范围、虚拟地址映射成物理地址以及分页机制**

  • 地址范围: 指处理器能够产生的地址集合,如一个32bit的处理器,其能产生的地址集合是0x0000 0000 ~ 0xffff ffff(4G),这个地址范围也称为虚拟地址空间,其中对应的地址为虚拟地址

  • 虚拟地址与物理地址: 与虚拟地址空间和虚拟地址相对应的是物理地址空间和物理地址;物理地址空间只是虚拟地址空间的一个子集。如一台内存为256MB的32bit主机,其虚拟地址空间是0 ~ 0xffffffff(4GB),

  • 物理地址空间范围是0 ~ 0x0fff ffff(256M)

四、分页机制

如果处理器没有MMU,或者有MMU但没有启用,CPU执行单元发出的内存地址将直接传到芯片引脚上,被内存芯片(以下称为物理内存,以便与虚拟内存区分)接收,这称为物理地址(Physical Address,以下简称PA),如下图所示:
在这里插入图片描述
如果处理器启用了MMU,CPU执行单元发出的内存地址将被MMU截获,从CPU到MMU的地址称为虚拟,而MMU将这个地址翻译成另一个地址发到CPU芯片的外部地址引脚上,也就是将VA映射成PA,,如下图:
在这里插入图片描述
linux使用MMU的机器都采用分页机制。虚拟地址空间以为单位进行划分,而相应的物理地址空间也被划分,其使用的单位称为页帧,页帧和页必须保持相同,因为内存与外部存储器之间的传输是以页为单位进行传输的

例如,MMU可以通过一个映射项将VA的一页0xb70010000xb7001fff映射到PA的一页0x20000x2fff,如果CPU执行单元要访问虚拟地址0xb7001008,则实际访问到的物理地址是0x2008。

​ 虚拟内存的哪个页面映射到物理内存的哪个页帧是通过页表(Page Table)来描述的,页表保存在物理内存中MMU会查找页表来确定一个VA应该映射到什么PA。

1.页表的概念

  • CPU在执行指令与数据时,获得的是虚拟内存的地址

但是CPU只能去物理内存寻址。此时,MMU就派上用场了。MMU负责,将虚拟地址,翻译成,真正运行时的物理地址。
MMU是如何将虚拟地址翻译成物理地址的,这个后面讲。现在先要了解一下交换区的概念。

交换区: 实际上就是一块磁盘空间(硬盘空间)。虚拟内存与物理内存映射的时候,是将虚拟内存的代码放到交换区中,以后在CPU想要执行相关的指令或者数据时,如果内存中没有,先去交换区将需要的指令与数据映射到物理内存,然后CPU再执行

虚拟内存与交换取的这种概念,实现了大内存需求量的(多个)进程,能够(同时)运行在较小的物理内存中。

到这里,我们应该大致明白了虚拟内存的作用与简单机制。还剩下MMU如何翻译虚拟地址为物理地址的,这放到最后讲解。现在先总结一下虚拟内存机制:

虚拟内存需要重新映射到物理内存
虚拟地址映射到物理地址中的实际地址
每次只有进程的少部分代码会在物理内存中运行
大部分代码依然位于磁盘中(存储器硬盘)

2.页式内存管理

上面笼统的介绍了虚拟内存的概念。接下来学习内存管理中的一种方式:页式内存管理

页式内存管理中我们需要了解:

  • 页的概念
  • 页表的概念
  • 缺页的概念与页命中的概念
  • 分配页面
  • 程序的局部性原则

1、页的概念
我们知道了交换区。我们知道交换区里面存放的是大部分的可执行代码与数据。而物理内存中,执行的是少部分的可执行代码与数据。
那么此时就需要从交换区获取程序的代码,将它拿到物理内存执行。那么一次拿多少代码过来呢?这是一个问题!
为了CPU的高效执行以及方便的内存管理,每次需要拿一个页的代码。这个页,指的是一段连续的存储空间(常见的是4Kb),也叫作块。
假设页的大小为P。在虚拟内存中,叫做虚拟页(VP)。从虚拟内存拿了一个页的代码要放到物理内存,那么自然物理内存也得有一个刚好一般大小的页才能存放虚拟页的代码。物理内存中的页叫做物理页(PP)
在任何时刻,虚拟页都是以下三种状态中的一种:

  • 未分配的:VM系统还未分配的页(或者未创建),未分配的页还没有任何数据与代码与他们相关联,因此也就不占用任何磁盘。
  • 缓存的: 当前已缓存在物理内存中的已分配页
  • 未缓存的:未缓存在物理内存中的已分配页

2、页表的概念
虚拟内存中的一些虚拟页是缓存在物理内存中被执行的。理所应当,应该有一种机制,来判断虚拟页,是否被缓存在了物理内存中的某个物理页上。如果不命中(需要一个页的代码,但是这个页未缓存在物理内存中),系统还必须知道这个虚拟页存放在磁盘上的哪个位置,从而在物理内存中选择一个空闲页或者替换一个牺牲页,并将需要的虚拟页从磁盘复制到物理内存中。

​ 这些功能,是由软硬件结合完成的。 包括操作系统软件,MMU中的地址翻译硬件,和一个存放在物理内存中的页表的数据结构。

​ 上一节说将虚拟页映射到物理页,实际上就是MMU地址翻译硬件将一个虚拟地址翻译成物理地址时,都会去读取页表的内容。操作系统负责维护页表的内容,以及在磁盘与物理内存之间来回传送页。

页表实际上就是一个数组。这个数组存放的是一个称为页表条目(PTE)的结构。虚拟地址空间的每一个页在页表中,都有一个对应的页表条目(PTE)。虚拟页地址(首地址)翻译的时候就是查询的各个虚拟页在页表中的PTE,从而进行地址翻译的。


总结

这次讲解了内存管理单元,页和页表的概念,下次将从Binder的用法和四层结构来讲解

<think>我们之前已经回答过Binder机制的基本概念,现在用户要求“详解”,因此需要更深入、全面地展开。 重点包括:Binder的架构层次(Java层、Native层、内核层)、核心组件(如ServiceManager、Binder驱动)、数据传输机制(如mmap零拷贝)、安全机制等。 同时注意保持结构清晰,必要时用代码示例说明。 最后按规则提出3-5个相关问题。</think>Binder 机制Android 系统的核心 IPC(进程间通信)框架,其设计解决了传统 Linux IPC 的性能和安全问题。以下是深度解析: ### 一、架构分层 1. **应用层(Java/Kotlin)** - 使用 AIDL 定义接口 - 自动生成 `Stub`(服务端)和 `Proxy`(客户端)类 ```java // 自动生成的 Proxy 类核心逻辑 public int add(int a, int b) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(a); _data.writeInt(b); mRemote.transact(TRANSACTION_add, _data, _reply, 0); int _result = _reply.readInt(); return _result; } ``` 2. **Native 层(C++)** - `libbinder` 库处理 JNI 桥接 - `BBinder`(服务端基类)和 `BpBinder`(客户端代理) ```cpp // Native 服务端实现 status_t MyService::onTransact(uint32_t code, const Parcel& data, Parcel* reply) { switch(code) { case ADD: { int a = data.readInt(); int b = data.readInt(); reply->writeInt(add(a, b)); return NO_ERROR; } } } ``` 3. **内核层(Binder 驱动)** - `/dev/binder` 设备文件 - 实现零拷贝的 mmap 机制 ```c // 驱动核心结构 (Linux kernel) struct binder_proc { struct hlist_node proc_node; struct rb_root threads; // Binder 线程红黑树 struct rb_root nodes; // Binder 节点红黑树 struct vm_area_struct *vma; // 内存映射区域 }; ``` ### 、核心工作机制 1. **零拷贝传输** - 通过 `mmap()` 创建共享内存区(通常 1MB) - 数据在用户空间打包为 Parcel 后直接写入内核缓冲区 - 接收方直接从映射区读取,避免数据复制 2. **线程池管理** - 每个 Binder 进程维护线程池(默认最大 16 线程) - 驱动使用红黑树调度请求 ```mermaid graph LR A[Client调用] --> B{Binder驱动} B --> C[线程池选择] C --> D[空闲线程?] D -->|是| E[分配执行] D -->|否| F[加入等待队列] ``` 3. **引用计数与生命周期** - 基于 `binder_ref` 结构管理跨进程对象引用 - 使用 `BC_ACQUIRE`/`BC_RELEASE` 指令控制对象生存期 ### 三、安全机制 1. **身份验证** - 驱动记录调用方 PID/UID - SELinux 策略检查 `binder{ call set }` 权限 2. **传输安全** - Parcel 数据自动签名验证 - 支持 `FLAG_CLEAR_BUF` 清除敏感数据 ### 四、性能优化设计 | 优化策略 | 效果提升 | 实现方式 | |-------------------|-----------------------|------------------------------| | 异步调用 | 减少 40% 延迟 | `FLAG_ONEWAY` 标志 | | 批量事务 | 吞吐量提高 3-5 倍 | `BINDER_TYPE_PTR` 聚合数据 | | 死亡通知 | 避免资源泄漏 | `linkToDeath()` 回调注册 | ### 五、典型工作流程 1. 服务端通过 `ServiceManager` 注册服务 2. 客户端获取服务代理对象 3. 客户端调用方法 → 代理封装 Parcel → 驱动传递 4. 服务端 `Binder` 线程解包执行 → 返回结果 5. 驱动将结果返回客户端代理
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值