news 2026/5/9 19:49:15

分页(Paging)完全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
分页(Paging)完全解析

分页(Paging)完全解析

🎯 一句话概括

分页= 把内存切成固定大小的,通过页表实现虚拟地址到物理地址的映射,让程序以为自己拥有连续的大内存,实际物理内存可以是不连续的碎片。

🏗️ 核心概念:从问题到解决方案

问题1:内存碎片化

物理内存使用情况: [程序A: 100K][空闲20K][程序B: 80K][空闲50K][程序C: 70K] 程序D需要120K,虽然总空闲有70K,但不连续!无法分配。

问题2:程序地址冲突

两个程序都认为自己的数据在地址0x4000,直接使用物理地址会冲突。

解决方案:分页!

虚拟地址空间 物理内存 0x0000-0x0FFF → 0x3000-0x3FFF (页框1) 0x1000-0x1FFF → 0xA000-0xAFFF (页框2) 0x2000-0x2FFF → 0x5000-0x5FFF (页框3)

关键:虚拟页连续,物理页框可以分散!

🔍 分页的核心组件

1. 页(Page)和页框(Page Frame)

  • :虚拟内存的固定大小块(通常4KB)
  • 页框:物理内存的固定大小块(同样4KB)
虚拟内存: 物理内存: ┌──────────┐ ┌──────────┐ │ 页 0 │ │ 页框 3 │ ├──────────┤ ├──────────┤ │ 页 1 │ 映射 │ 页框 7 │ ├──────────┤ → ├──────────┤ │ 页 2 │ │ 页框 1 │ ├──────────┤ ├──────────┤ │ 页 3 │ │ 页框 9 │ └──────────┘ └──────────┘

2. 页表(Page Table)

页表就是映射字典:虚拟页号 → 物理页框号

// 简化的页表条目(PTE)structpage_table_entry{uint32_tpresent:1;// 页是否在内存中uint32_trw:1;// 可读/写uint32_tuser:1;// 用户态可访问uint32_taccessed:1;// 是否被访问过(用于替换算法)uint32_tdirty:1;// 是否被修改过uint32_tunused:7;// 保留位uint32_tframe:20;// 物理页框号(真正的映射!)};

3. 地址转换过程

虚拟地址 = 页号 + 页内偏移

32位虚拟地址:0x12345678 二进制:0001 0010 0011 0100 0101 0110 0111 1000 └───────┬──────┘ └─────────┬─────────┘ 页号(20位) 页内偏移(12位) = 0x12345 = 0x678

转换公式

物理地址 = (页表[页号].物理页框号 × 页大小) + 页内偏移 = (frame × 4096) + offset

🔄 MMU的地址转换过程

单级页表示例

虚拟地址: 0x12345678 1. 提取页号: 0x12345 2. 查找页表: 页表[0x12345] = {frame=0x54321, present=1} 3. 计算物理地址: (0x54321 × 4096) + 0x678 = 0x54321678

实际多级页表(x86为例)

现代系统使用多级页表节省空间:

虚拟地址: 0x80123456 ┌──────────────┬────────────┬────────────┐ │ 页目录索引(10)│ 页表索引(10) │ 偏移量(12) │ │ 0x200 │ 0x123 │ 0x456 │ └──────────────┴────────────┴────────────┘ 转换步骤: 1. CR3 → 页目录基址 2. 页目录[0x200] → 页表基址 3. 页表[0x123] → 物理页框号 4. 物理页框号×4096 + 0x456 → 物理地址

🏗️ 分页系统的完整架构

内存中的数据结构

// 完整的内存管理数据结构链structmm_struct{// 进程内存描述符pgd_t*pgd;// 页全局目录(一级页表)structvm_area_struct*mmap;// 虚拟内存区域链表unsignedlongstart_code,end_code;// 代码段unsignedlongstart_data,end_data;// 数据段unsignedlongstart_brk,brk;// 堆unsignedlongstart_stack;// 栈};// 页表层次结构(x86-64 4级页表)pgd_t// 页全局目录 (Page Global Directory)p4d_t// 页4级目录 (Page 4th Directory)pud_t// 页上级目录 (Page Upper Directory)pmd_t// 页中间目录 (Page Middle Directory)pte_t// 页表条目 (Page Table Entry)

💡 分页的五大优势

1. 解决外部碎片

物理内存: [空闲] [程序A] [空闲] [程序B] [空闲] 分页后:程序C需要3页 → 可以分配页框1、3、5

2. 简化内存分配

// 分配内存变得简单!void*allocate_pages(intnum_pages){for(每个空闲页框){标记为已用,返回虚拟地址}}

3. 共享内存容易

进程A页表: 虚拟页X → 物理页框100 进程B页表: 虚拟页Y → 物理页框100(同一物理页!) 共享库代码只需一份物理副本!

4. 权限控制精细

// 每个页独立的权限.text页: 只读+可执行.data页: 读写+不可执行 栈页: 读写+不可执行+向下增长

5. 支持虚拟内存

// 页可以不在物理内存中!structpage_table_entry{uint32_tpresent:1;// 0 = 不在内存uint32_tswap_location:31;// 在磁盘上的位置};

⚡ 性能问题与解决方案

问题:页表太大!

32位系统,4KB页: 虚拟地址空间:2^32 = 4GB 页数:4GB/4KB = 1M页 每个PTE:4字节 页表总大小:1M × 4B = 4MB(每个进程!) 100个进程 → 400MB页表!不现实!

解决方案1:多级页表

二级页表: 第一级:页目录(1024项 × 4B = 4KB) 第二级:只有用到的页表才分配(每张4KB) 优点:稀疏地址空间节省大量内存 缺点:两次内存访问(TLB缓解)

解决方案2:TLB(快表)

// Translation Lookaside Buffer// CPU缓存,存储最近使用的页表映射structtlb_entry{uint64_tvirtual_page;// 虚拟页号uint64_tphysical_frame;// 物理页框号bool valid;// 是否有效};// 命中率通常>99%,使分页几乎无开销

解决方案3:大页(Huge Pages)

普通页:4KB 大页:2MB或1GB 优点: - 减少TLB压力(一个条目映射更大区域) - 减少页表层级 - 提高性能(数据库、虚拟化常用)

🔧 实际实现:缺页处理

缺页中断流程

// 当访问present=0的页时触发voidpage_fault_handler(uintptr_tfault_addr,interror_code){// 1. 检查地址是否合法if(!is_valid_address(fault_addr)){kill_process(current,SIGSEGV);// 段错误return;}// 2. 分配物理页框structpage*page=alloc_page();// 3. 可能需要从磁盘加载数据if(is_swapped_out(fault_addr)){load_from_swap(page,fault_addr);}elseif(is_file_mapped(fault_addr)){load_from_file(page,fault_addr);}else{// 零页(第一次访问的匿名页)memset(page,0,PAGE_SIZE);}// 4. 更新页表update_page_table(fault_addr,page,PROT_READ|PROT_WRITE);// 5. 返回,重新执行触发缺页的指令}

🎮 完美类比:图书馆与书

传统连续内存 = 传统图书馆

书架必须连续:你要借10本书,必须找到连续10个空位 问题:即使总空位很多,但不连续就借不了

分页系统 = 现代图书馆

每本书有唯一编号(虚拟地址) 图书馆员(MMU)有目录(页表): - 你的"书1" → 实际在"A架3层" - 你的"书2" → 实际在"G架7层" - 你的"书3" → 实际在"D架1层" 对你来说:书1、2、3在"你的书架"上是连续的 实际上:它们分散在图书馆各处

缺页处理 = 调书流程

你要读"书5",但不在手边(不在内存) 1. 去仓库(磁盘)找到书5 2. 放到某个空位(物理页框) 3. 更新目录(页表) 4. 把书给你读

📊 页表数据结构代码示例

PintOS中的页表管理

// PintOS的页表条目(32位系统)structpage_table_entry{uint32_tpresent:1;// 是否在内存uint32_trw:1;// 可写uint32_tuser:1;// 用户态可访问uint32_taccessed:1;// 被访问过uint32_tdirty:1;// 被修改过uint32_tunused:6;// 未使用uint32_tframe:20;// 物理页框号};// PintOS页目录(一级)uint32_t*page_directory;// 1024个条目 × 4字节// 设置页表voidpagedir_set_page(uint32_t*pd,void*upage,void*kpage,bool rw){uint32_tpde_idx=(uintptr_t)upage>>22;// 高10位uint32_tpte_idx=((uintptr_t)upage>>12)&0x3FF;// 中间10位// 确保页表存在if(!(pd[pde_idx]&PTE_P)){uint32_t*pt=palloc_get_page(PAL_ZERO);pd[pde_idx]=(uintptr_t)pt|PTE_P|PTE_U|PTE_W;}// 设置PTEuint32_t*pt=(uint32_t*)(pd[pde_idx]&~0xFFF);pt[pte_idx]=(uintptr_t)kpage|PTE_P|(rw?PTE_W:0)|PTE_U;}

🚀 现代扩展功能

1. 地址空间布局随机化(ASLR)

// 每次运行时,内存布局随机化,增加安全性mmap_base=random_offset();// 栈、堆、库的地址随机

2. 内存去重(KSM)

// 内核同页合并:相同内容的页只保留一份if(page_content_equals(page1,page2)){map_both_to_same_frame(page1,page2);free_one_page_frame();}

3. 透明大页(THP)

// 自动将连续的4KB页合并为2MB大页if(连续16个页被频繁访问){合并为大页();更新TLB();}

⚠️ 常见问题与解答

Q:分页与分段有什么区别?

分段: 分页: - 变长块 - 固定大小页 - 外部碎片 - 无外部碎片 - 硬件复杂 - 硬件简单 - 保护基于段 - 保护基于页 现代OS:分段+分页结合(x86)

Q:页表存在哪里?

1. 在内存中(由操作系统管理) 2. CR3寄存器指向当前进程的页表基址 3. TLB缓存热门映射(CPU硬件)

Q:切换进程时页表如何处理?

// 上下文切换时switch_mm(old_mm,new_mm){if(old_mm!=new_mm){write_cr3(new_mm->pgd);// 切换页表flush_tlb();// 清空TLB(或使用ASID避免)}}

💎 总结:分页的核心价值

三个核心特性

  1. 透明性:程序无需知道分页存在
  2. 灵活性:虚拟连续 ↔ 物理分散
  3. 保护性:每页独立权限

一个核心机制

虚拟地址 → [MMU+页表] → 物理地址 ↓ ↓ 程序视角的地址 实际内存位置

在PintOS中的实践

在Project 3(虚拟内存)中,您将:

  1. 实现页表管理
  2. 处理缺页异常
  3. 实现页面置换算法
  4. 支持栈增长

最终理解:分页是现代操作系统的内存魔术——通过巧妙的映射,让有限、碎片化的物理内存,为每个程序提供看似无限、连续、私有的地址空间。这是虚拟化技术的基石,也是理解现代计算的关键!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 6:08:05

GESP Python 编程一级教材之 13 掌握模块的导入方法(教程含历年试题解析)

系列文章 《GESP系列教程之 什么是GESP?》 《GESP 认证标准之 Python 编程一级标准(考试大纲与要求含考试真题)》 《GESP 认证标准之 Python 编程二级标准(考试大纲与要求含考试真题)》 《GESP 认证标准之 Python 编程三级标准(考试大纲与要求含考试真题)》 《GESP …

作者头像 李华
网站建设 2026/5/3 10:15:53

多智能体实战指南:9种模式打造高效AI应用

想要构建一个智能体应用,最重要的是什么?可能很多人首先会想到要选择一个性能强大的大模型。 这个回答没错,毕竟当前的LLM Based Agent哪能缺少LLM的支撑。但事实却是,很多基于先进大模型构建的智能体没能体现出应用效果&#xff…

作者头像 李华
网站建设 2026/5/2 3:54:17

微信小程序 PHP_uniapp的智能购物助手与价格比较系统的设计与实现_77607w0u

摘要 该研究聚焦于基于微信小程序的智能购物助手与价格比较系统的设计与实现,采用PHP后端与UniApp前端框架相结合的技术方案。系统旨在解决用户在多平台购物时面临的比价效率低、信息分散等问题,通过智能化的商品检索与价格分析功能,提升用户…

作者头像 李华
网站建设 2026/5/9 15:32:30

OddAgent:一个通用的意图、指令识别框架

想自己动手来手搓一个完全属于你自己的“小爱同学”、“小艺”吗?如果有你这么一个想法,而又不知道该如何开始的话,那么OddAgent项目可以成为你非常容易上手的开源项目。 本来这个功能是小落同学在2024年初就已经支持,由于前阵子…

作者头像 李华
网站建设 2026/5/3 11:48:53

【读论文】ASR大模型动态热词新方案

摘要 大型语言模型(LLMs)以其卓越的通用知识和推理能力重塑了 AI 领域,但它们在处理特定领域或用户的专有词汇(即“热词”)时,往往表现出“选择性失忆”。传统的 RAG 或 Prompting 方案在实时性和准确性上存在瓶颈。阿里的《Hotword Model for Large Models》论文提出了…

作者头像 李华
网站建设 2026/4/29 22:53:36

工业摄像头是专为工业场景设计的高性能视频图像

工业摄像头是专为工业场景设计的高性能视频图像采集设备,其工作原理、核心部件、技术特性、应用领域及工作流程如下:工作原理工业摄像头通过光电转换原理,将光信号转化为电信号。当被摄物体的光线通过镜头聚焦到感光传感器(如CCD或…

作者头像 李华