揭秘操作系统内存管理:从虚拟地址到页表和缺页异常的全流程解析
分页式存储管理通过虚拟地址与页表实现进程内存隔离,将逻辑地址分页映射到非连续物理内存。物理内存由struct page管理,采用多级页表(如二级页表)降低内存开销,CPU通过页目录和页表索引定位物理地址,TLB加速转换。缺页异常处理虚拟地址未映射情况,包括磁盘加载的硬缺页、映射未建立的软缺页及非法访问的无效缺页,确保内存高效利用与系统稳定。
💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!
👍点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!
🚀分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对Linux OS感兴趣的朋友,让我们一起进步!
一. 分页式存储管理
分页式存储管理是操作系统中用于高效管理内存资源的一种关键技术,其核心思想是将程序的逻辑地址空间与物理内存空间解耦,通过非连续映射实现灵活的内存分配。
1.1 虚拟地址与页表的由来
-
虚拟地址的引入是为了实现进程的虚拟化,使每个进程认为它拥有整个计算机的内存,而不需要知道其他进程的内存使用情况。具体来说:
-
虚拟内存:虚拟内存允许程序使用比实际物理内存更多的内存。操作系统通过虚拟地址空间映射到实际的物理内存,管理着程序的内存访问。
-
隔离性与保护性:每个进程有自己的虚拟地址空间,这样即使在多个进程同时运行的情况下,它们之间不会互相干扰。虚拟地址的使用为进程提供了更高的安全性和保护。
-
页表是虚拟内存管理的关键数据结构,用于将虚拟地址映射到物理地址。它的引入解决了如何高效管理和查找虚拟内存和物理内存之间的映射关系。
-
分页机制:分页是一种内存管理方案,它将虚拟内存划分为固定大小的块,称为“页”(Page)。而物理内存则被分成大小相同的块,称为“页框”(Page Frame)。页表就是用来存储虚拟页和物理页框之间映射关系的数据结构。
-
页表作用:当一个进程访问某个虚拟地址时,操作系统通过页表查找该虚拟地址对应的物理地址。如果虚拟地址所在的页不在物理内存中(即发生了页缺失),操作系统会从磁盘加载该页到物理内存中。从一定程度上说保护物理内存。
总结:将虚拟内存下的逻辑地址空间分为若⼲⻚,将物理内存空间分为若⼲⻚框,通过
⻚表便能把连续的虚拟内存,映射到若⼲个不连续的物理内存⻚。
1.2 物理内存管理
比如说一个可用物理内存有4GB空间。一个页框大小为4KB,页框的个数:4GB/4KB = 1048576。有这么多页框,操作系统要把它管理起来,先描述,在组织,内核用该数据结构struct page{}描述每个物理页。
里面重要的参数:
- flags(页面标志位)作用:存储页面的状态和属性,通过位掩码实现高效管理。
- _mapcount(页表项计数器)作用:记录页表中有多少项指向该页面。
- _count(引用计数器)作用:原子计数器,跟踪页面引用次数。
- 机制:
- 页面被映射到页表、锁定(pin)或由内核其他部分使用时,计数器增加。
- 引用释放时计数器减少,归零后页面可被回收。
1.3 页表
页表中的每一个表项指向一个物理页的起始地址。物理内存存在多少个页框就要有多少个页表相对应进行映射。如下图:
问题1:若虚拟地址空间中存在未映射到物理内存的区域(如内核保留区、空洞等),页表项可能未完全覆盖整个4GB空间,导致访问未映射区域时触发缺页异常。
问题2:若某些页表项未正确初始化(如全0或无效物理地址),访问对应虚拟页面时会导致错误。
物理地址对齐:物理页起始地址需按页大小对齐(4KB对齐),若页表项中的物理地址未对齐,可能导致硬件异常。
为解决上述问题,操作系统使用更高级的技术,多级页表进行映射。
将需要进行映射的页表填充虚拟地址到物理地址的映射。
1.4 页目录结构
每一个页框都被一个页表中的表项,那么1024个页表也需要被管理起来。管理页表的的表称之为页目录,形成二级页表。如下图:
所有页表的起始地址被页目录指向,二页目录表的起始地址被CR3寄存器所指向,这个寄存器会保存当前正在进行任务的页目录起始地址。
1.5 两级页表地址转换
1.5.1 两级页表的基本结构
- 两级页表由 页目录(Page Directory) 和 页表(Page Table) 组成:
- 页目录:包含多个页目录项(Page Directory Entry, PDE),每个PDE指向一个页表。
- 页表:包含多个页表项(Page Table Entry, PTE),每个PTE指向一个物理页帧。
在32位x86架构中,虚拟地址通常按以下格式划分:
| 10位页目录索引 | 10位页表索引 | 12位页内偏移 |
- 页目录索引:用于在页目录中定位对应的页表。
- 页表索引:用于在页表中定位对应的物理页帧。
- 页内偏移:表示物理页帧内的具体字节位置。
1.5.2 地址转换过程
当CPU访问一个虚拟地址时,地址转换过程如下:
以虚拟地址 0x12345678 的二进制为:0001 0010 0011 0100 0101 0110 0111 1000为例。
- CR3寄存器读取页目录的起始地址,根据虚拟地址前10位作为一级页号查对应页目录表的起始地址,再根据后10位查找对应页表中的物理内存的起始地址。
- 提取页内偏移,将虚拟地址后12位作为物理内存的偏移量。
- 计算物理地址:将PTE中的物理页帧地址左移12位(或乘以4KB),与页内偏移相加,得到最终物理地址:
物理地址 = (物理页帧地址 << 12) | 页内偏移
多级存储降低内存要求,但是降低查询效率为了解决该问题,引入TLB。为减少多次内存访问的开销,CPU使用 TLB(Translation Lookaside Buffer) 缓存最近的页表映射:
- TLB命中:直接使用缓存的物理地址,无需访问页目录和页表。
- TLB未命中:执行完整的两级页表查找,并更新TLB。
如图:
过程:首先从TLB查看缓存地址,如果有直接进行映射,否则在从页表中查表进行映射,在添加至TLB缓冲中,减少查询效率,提升效率。通过这种分层设计,两级页表在内存效率和地址空间扩展性之间实现了平衡,是现代操作系统内存管理的核心机制之一。
1.6 缺页异常
1.6.1 缺页异常的本质
缺页异常(Page Fault)是操作系统内存管理的核心机制之一,指进程访问的虚拟地址未映射到物理内存时,由CPU的内存管理单元(MMU)触发的中断。其本质是虚拟内存系统通过“按需调页”实现内存隔离与扩展的关键手段。
1.6.2 缺页异常的触发场景
-
硬缺页(Hard Page Fault)
-
物理页缺失:页面未在物理内存中,需从磁盘加载(如程序首次启动时加载代码段)。
-
磁盘I/O开销:以机械硬盘为例,平均寻道时间约8.5ms,远高于内存访问(数十纳秒)。
典型场景:进程访问被交换到Swap分区的页面,或首次访问通过mmap映射的文件。 -
软缺页(Soft Page Fault)
-
映射未建立:页面在物理内存中,但未在进程页表中注册(如共享内存区域)。
-
零成本修复:仅需更新页表项,无需磁盘操作。
典型场景:多进程共享库映射,或页面被暂时移出工作集但未换出。 -
无效缺页(Invalid Page Fault)
-
非法访问:如越界访问、空指针解引用,或写入只读页面(如代码段)。
-
系统响应:触发SIGSEGV信号终止进程,或内核日志报错。
1.6.3 缺页异常的处理流程
以Linux系统为例,处理流程如下:
- 合法性检查
- 通过CR2寄存器获取触发异常的虚拟地址。
- 检查地址是否属于进程地址空间:
- 越界访问:直接终止进程(如访问NULL指针)。
- 权限不足:如尝试写入只读页面,触发写时复制(COW)或终止进程。
- 物理页分配
- 内存不足时:执行页面置换(如LRU算法),可能将脏页写回磁盘。
- 匿名页分配:如进程堆/栈扩展,或malloc首次访问。
- 文件页分配:如加载可执行文件代码段或mmap映射的文件。
- 磁盘I/O(仅硬缺页)
- 从Swap分区或文件系统读取数据到物理页。
- 预读优化:Linux可能预读相邻页面以减少未来缺页。
- 页表更新
- 写入物理页号到页表项(PTE),并设置存在位(Present Bit)。
- 写时复制(COW):父子进程共享页被修改时,复制页面并更新映射。
- 指令恢复执行
- 重新执行触发缺页的指令,此时映射已建立。
理解缺页异常机制,是优化高负载系统性能、调试内存相关错误(如段错误)的关键基础。
二. 最后
分页式存储管理通过虚拟地址与页表实现进程内存隔离,将逻辑地址分页映射到非连续物理内存。物理内存由struct page管理,采用多级页表(如二级页表)降低内存开销,CPU通过页目录和页表索引定位物理地址,TLB加速转换。缺页异常处理虚拟地址未映射情况,包括磁盘加载的硬缺页、映射未建立的软缺页及非法访问的无效缺页,确保内存高效利用与系统稳定。