页式虚存原理到 Linux 0.11 实践:解析 2 种缺页异常与 do_no_page 函数

发布时间:2026/7/8 21:08:50
页式虚存原理到 Linux 0.11 实践:解析 2 种缺页异常与 do_no_page 函数 页式虚存原理到 Linux 0.11 实践解析 2 种缺页异常与 do_no_page 函数1. 页式虚存的核心机制现代操作系统的内存管理离不开页式虚存技术。想象一下当你在电脑上同时打开多个大型软件时物理内存可能早已耗尽但系统依然能流畅运行——这正是页式虚存创造的魔法。其核心思想是将物理内存和磁盘空间统一管理为每个进程提供连续的虚拟地址空间。页式管理的关键数据结构包括页目录Page Directory顶级映射表每个进程独立拥有页表Page Table二级映射结构存储虚拟页到物理页框的映射页表项PTE包含物理页框号、访问权限等关键信息地址转换过程遵循以下步骤CPU 生成虚拟地址MMU 提取页目录索引高 10 位通过页目录找到页表基址提取页表索引中间 10 位定位页表项结合页内偏移低 12 位得到物理地址// Linux 0.11 中的地址转换示意 #define PAGE_DIR_INDEX(vaddr) (((vaddr)22) 0x3FF) #define PAGE_TABLE_INDEX(vaddr) (((vaddr)12) 0x3FF) #define PAGE_OFFSET(vaddr) ((vaddr) 0xFFF)2. 缺页异常的分类与触发场景当程序访问的虚拟地址没有有效物理映射时CPU 会触发缺页异常Page Fault。Linux 0.11 主要处理两种典型场景2.1 代码/数据缺页特征访问的虚拟地址属于进程地址空间对应内容实际存在于磁盘可执行文件或交换区页表项存在但被标记为不存在Present0处理流程分配空闲物理页框从磁盘读取对应内容更新页表建立映射重新执行触发异常的指令2.2 零页缺页BSS段特征访问未初始化的全局变量区域.bss磁盘上无实际存储内容页表项完全不存在处理差异直接映射到系统预分配的零页全0页面无需磁盘I/O操作采用写时复制COW机制对比维度代码/数据缺页零页缺页触发场景访问已分配但未加载的页访问未初始化数据磁盘内容存在有效数据无处理代价需要磁盘I/O仅内存操作最终页表项值指向新分配的物理页指向共享的零页典型应用程序代码段、堆栈.bss段、匿名内存3. Linux 0.11 的缺页处理核心do_no_page在Linux 0.11内核中do_no_page是处理缺页异常的核心函数其逻辑流程如下// 简化的处理流程 void do_no_page(unsigned long error_code, unsigned long address) { // 1. 检查地址合法性 if (address TASK_SIZE) do_exit(SIGSEGV); // 2. 计算页偏移 unsigned long page address PAGE_MASK; // 3. 获取页目录项 pgd_t *pgd pgd_offset(current, address); // 4. 检查是否为BSS段缺页 if (pgd_none(*pgd)) { // 零页处理 get_empty_page(address); return; } // 5. 常规缺页处理 unsigned long tmp get_free_page(); if (!tmp) oom(); // 6. 从磁盘读取内容 unsigned long from page current-start_code; bread_page(tmp, current-executable-i_dev, from); // 7. 建立页表映射 put_page(tmp, address); }关键操作解析地址验证确保访问地址在用户空间范围内零页检测通过检查页目录项判断是否为BSS段物理页分配调用get_free_page从内存管理子系统获取空闲页磁盘读取对于可执行文件缺页计算磁盘位置并读取映射建立通过put_page设置页表项权限和映射关系4. 实战分析两种缺页的处理差异通过对比Linux 0.11处理/bin/sh时的两种缺页场景我们可以深入理解其实现差异4.1 代码段缺页实例当执行/bin/sh时首次触发代码缺页线性地址0x8000000代码段起始页目录项0x80初始值为0处理过程分配物理页框如0xffa000从/bin/sh文件读取第2块内容偏移1KB处设置页表项为0xffa007Present|RW|User# 调试观察点模拟环境 (gdb) break do_no_page (gdb) x/16xb 0xffa000 # 查看新页内容4.2 BSS段缺页实例访问/bin/sh的未初始化数据时线性地址0x8032000BSS起始VMA页表项0xff70c8初始值为0处理差异直接映射到零页0x00ff2000设置页表项为0x00ff2007不执行磁盘读取操作// 零页处理关键代码 static void get_empty_page(unsigned long address) { unsigned long tmp ZERO_PAGE; // 系统零页地址 put_page(tmp, address); }5. 性能优化与高级特性虽然Linux 0.11的实现较为基础但现代系统在此基础上发展出多项优化按需调页仅在访问时加载页面减少启动延迟预读取根据访问模式提前加载相邻页面页缓存缓存频繁访问的磁盘页减少I/O交换空间将不活跃页面换出到磁盘透明大页使用2MB/1GB大页减少TLB缺失注意在修改页表映射时需要同步刷新TLB缓存。x86架构通过invlpg指令实现现代处理器还提供全局TLB刷新操作。通过深入理解这些基础机制开发者可以更好地处理内存相关异常优化程序的内存访问模式。在实际系统编程中合理控制缺页频率对性能至关重要——频繁的缺页中断会导致明显的性能下降这也是为什么数据结构的内存局部性Locality如此重要。