操作系统-虚拟内存篇

虚拟内存

虚拟内存的设计目标是避免程序直接操作物理内存------ 若程序直接引用绝对物理地址,可能因地址冲突导致程序互相干扰,甚至引发系统崩溃。其实现核心是 "地址隔离与映射":

虚拟内存的概念是由操作系统为每个程序分配一套虚拟地址,然后再由一个中间结构来记录虚拟地址到真实地址的映射关系

由此我们得出结论

  • 程序使用的内存地址为虚拟内存地址
  • 硬件使用的内存地址为物理内存地址

操作系统使用CPU芯片中的内存管理单元(MMU)实现虚拟地址和真实地址的映射

那么MMU是怎么实现这种映射关系的呢

是通过内存分段和内存分页来实现的

内存分段

内存分段的核心思路是:按程序的功能模块(如代码段、数据段、栈段)划分虚拟地址空间,每个模块对应一个 "段",再通过段表实现虚拟段到物理段的映射

虚拟地址通过段表和物理地址做映射,通过段号作为索引快速找到段基地址和段界限等信息。段基地址就是当前段在物理内存中的起始位置,加上偏移量就是这个虚拟地址需要的全部真实物理地址空间。然而分段的方式会导致2个问题的出现

  1. 内存碎片
  2. 内存交换效率低

内存碎片的产生这里就不细讲了,参考JVM中的标记-清除算法大概心里就清楚了。

不过内存碎片分为外部内存碎片和内部内存碎片,对于分段来说,是按程序大小分配的段空间,一般不存在内部内存碎片,只会存在外部内存碎片,也就是段段之间的空间无法再分配导致浪费的情况。

为了解决内存碎片的问题,引入了内存交换方法

内存交换的本质如下,a和b两个段之间有100m空间碎片无法被利用,将b传入磁盘再重传内存中,重传的时候直接紧跟a后面实现,对于这个内存交换空间也成为Swap空间,是从磁盘划分出来用于磁盘和内存的空间交换。

从这里我们大概也能知道为什么交换效率低了,空间碎片不可避免且如果启动关闭程序变得频繁,内存碎片增多,交换次数上升,而交换是通过磁盘IO来操作的,一切就不言而喻了。于是出现了内存分页

内存分页

分页的本质是将虚拟内存和物理内存也按照固定大小进行连续划分,并且一一映射,减少了内存交换的数据数量。使用段的时候,每次交换都需要以段为单位,现在交换以页为单位

如果进程需要的虚拟内存无法找到对应物理内存,系统就会产生缺页异常来进入系统内核态,由内核分配物理空间并更新页表,恢复程序正常运行

内存分页机制下,分配内存的基本单位为页,因此可能会出现内部内存碎片的问题,不过也问题不大,页本身就很小,即使有大量这点碎片无足轻重。并且每个程序只能产生1个内部内存碎片,因此不可能出现碎片过多程序拖慢的问题

分页的映射实际上是和分段差不多的,不过做了优化,由于页很小,因此页数是很多的,如果做一一映射会导致大量内存用来存储页表

为了解决上诉问题,又引入了多级页表的概念,概念图如下

一级页表的痛点

以 32 位系统、4KB 页大小为例:

  • 虚拟地址空间总大小 = 4GB,每页 4KB,因此虚拟页数量 = 4GB / 4KB = 1,048,576 个(约 100 万);
  • 若采用 "一级页表",每个页表项需 4 字节(存储物理页号等信息),则一级页表总大小 = 100 万 × 4 字节 = 4MB。

这会导致两个问题:

  1. 空间浪费:每个程序无论实际使用多少虚拟页,都需分配 4MB 页表空间
  2. 内存占用高:若系统同时运行 100 个程序,仅页表就需占用 400MB 物理内存,资源消耗过大。

多级页表的优化逻辑

多级页表的核心是 "按需创建下级页表"------ 将虚拟地址的 "页号" 拆分为多个层级,仅为已使用的虚拟页创建下级页表,未使用的层级无需分配空间。以 "二级页表" 为例:

  1. 虚拟地址拆分:将 32 位虚拟地址拆分为 "一级页号(10 位) + 二级页号(10 位) + 页内偏移量(12 位)";
  2. 一级页表作用:仅存储 "一级页号 → 二级页表基地址" 的映射,每个一级页表项对应一个二级页表;
  3. 二级页表作用:存储 "二级页号 → 物理页号" 的映射,仅为已使用的虚拟页创建二级页表项。

(3)二级页表的空间优势

仍以 32 位系统、4KB 页大小为例:

  • 一级页表项数量 = 2^10 = 1024 个,总大小 = 1024 × 4 字节 = 4KB;
  • 每个二级页表项数量 = 2^10 = 1024 个,总大小 = 4KB;若程序仅使用 10 个虚拟页,则仅需创建 1 个二级页表(4KB),无需创建其他 1023 个二级页表;
  • 程序总页表空间 = 一级页表(4KB) + 1 个二级页表(4KB) = 8KB,远小于一级页表的 4MB,空间利用率大幅提升。

段页式内存管理

分段的优势是 "按功能模块隔离地址",分页的优势是 "无外部碎片、交换效率高"。通过 "段页式内存管理"------ 先分段,再分页,实现完美结合

1. 段页式的核心逻辑

将虚拟地址空间先按 "段" 划分(如代码段、数据段),每个段再按 "页" 划分(固定大小的虚拟页),物理内存仅按 "页" 划分。映射过程分为三步:

  1. 段表查询:虚拟地址中的 "段号" 查询段表,获取该段的 "页表基地址"(每个段对应一个独立的页表);
  2. 页表查询:用虚拟地址中的 "页号" 查询该段对应的页表,获取 "物理页号"
  3. 计算物理地址:物理地址 = 物理页号 × 页大小 + 页内偏移量。

2. 段页式的优势与应用

  1. Windows、Linux、macOS 这些主流操作系统均采用段页式内存管理,平衡了地址隔离性、内存利用率与访问性能;
  2. 只有很少的内部内存碎片且提高了交换效率,并且有了分段的优势,程序之间的隔离性提高
相关推荐
Funcy2 小时前
XxlJob源码分析01:环境准备
java
the beard3 小时前
Feign整合Sentinel实现服务降级与Feign拦截器实战指南
java·spring·sentinel
THMAIL3 小时前
攻克 Java 分布式难题:并发模型优化与分布式事务处理实战指南
java·开发语言·分布式
小沈同学呀3 小时前
使用Java操作微软 Azure Blob Storage:上传和下载文件
java·microsoft·azure
czhc11400756633 小时前
Linux 830 shell:expect,ss -ant ,while IFS=read -r line,
linux·运维·r语言
滴滴滴嘟嘟嘟.4 小时前
嵌入式Linux驱动开发:蜂鸣器驱动
linux·运维·驱动开发
梅见十柒4 小时前
UNIX网络编程笔记:共享内存区和远程过程调用
linux·服务器·网络·笔记·tcp/ip·udp·unix
CYRUS_STUDIO5 小时前
一步步带你移植 FART 到 Android 10,实现自动化脱壳
android·java·逆向
练习时长一年5 小时前
Spring代理的特点
java·前端·spring
CYRUS_STUDIO5 小时前
FART 主动调用组件深度解析:破解 ART 下函数抽取壳的终极武器
android·java·逆向