【Linux —— 虚拟地址空间与二级页表的管理与转换】

Linux 页目录页表寻址

1.引言

概念

虚拟地址空间操作系统 用来给每个运行中的进程提供独立地址空间的机制。通过虚拟地址空间,操作系统可以让每个进程感觉自己拥有独立的、完整的内存空间,即使物理内存较小。虚拟地址空间的存在使得进程间的内存隔离得以实现,避免了不同进程之间的内存冲突。

为什么需要虚拟地址空间以及它在内存管理中的重要性


虚拟地址空间的主要目的包括以下几个方面:

  • 内存保护:每个进程有独立的虚拟地址空间,避免了进程间互相干扰。如果没有虚拟地址空间,一个进程可能会意外访问或修改另一个进程的内存数据,这会导致严重的错误。
  • 内存管理灵活性:虚拟地址空间允许操作系统将物理内存以更灵活的方式分配给进程。操作系统可以将不连续的物理内存区域映射到连续的虚拟地址空间,从而更有效地利用内存。
  • 简化编程:程序员可以假定程序运行在一个连续的内存空间中,而无需关心物理内存的布局。操作系统负责将虚拟地址转换为物理地址,使得编程更加简单和抽象。
  • 分页和交换:通过虚拟地址空间,操作系统可以将不活跃的页面交换到磁盘上,从而腾出物理内存供其他进程使用。这种技术称为虚拟内存,它极大地扩展了可用内存的容量。

2.虚拟地址到物理地址的映射

2.1 虚拟地址的结构

在32位系统中,虚拟地址由32位二进制数表示。这些位被划分为三部分:

  • 前10位(页目录索引,Page Directory Index):用来索引页目录表中的页目录项。页目录项指向页表的物理地址。
  • 中间10位(页表索引,Page Table Index):用来索引页表中的页表项。页表项指向具体的物理页框(Page Frame)。
  • 最后12位(页内偏移量,Page Offset):用来定位物理页框内的具体地址。

这种结构允许每个进程的虚拟地址空间可以映射到不同的物理内存地址,从而实现内存隔离和灵活管理。

2.2 页目录和页表的作用

页目录和页表是将虚拟地址映射到物理地址的核心组件:

  • 页目录 :包含多个页目录项 (Page Directory Entry, PDE),每个页目录项指向一个页表的起始地址。页目录可以视为虚拟地址空间的"一级索引"。

  • 页表 :包含多个页表项 (Page Table Entry, PTE),每个页表项指向一个物理页框的起始地址。页表可以视为"二级索引",进一步将虚拟地址映射到物理地址。

3.页框/页帧/页面的概念

在之前的文件博客中我们了解到了IO的基本单位是4KB,同时OS进行内存管理,不是以字节位单位的,而是以内存块为单位的,默认的大小是4KB。所以OS对内存的管理工作,基本单位是4KB。

什么是页框/页帧

页框或页帧 是物理内存中的固定大小的块 ,通常与虚拟内存中页面(Page) 大小相同。在现代计算机系统中,页面大小通常为4KB(4096字节),因此页框的大小也通常为4KB。
页框 是操作系统用来存储页面 的基本单位。物理内存中的页框用来存储从磁盘载入的页面数据并将它们映射到虚拟地址空间中。


什么是页面
页面(Page)虚拟内存最小 管理单位。页面是将虚拟内存划分为等大小的块,通常与物理内存中的页框(Page Frame)大小一致。页面机制是实现虚拟内存的重要基础,通过将虚拟地址映射到物理地址,使得程序可以在不连续的物理内存上运行,提供内存保护和更高的内存利用效率。

4.二级页表介绍

4.1 二级页表的概念

二级页表 (Two-Level Page Table)是一种内存管理结构,主要用于减少虚拟地址空间管理中的内存开销 。在32位系统中,虚拟地址被分为三个部分:页目录索引、页表索引和页内偏移量 。二级页表通过引入页目录页表两级结构,减少了直接使用单级页表所需的巨大内存。

4.2二级页表的寻址

假设我们有一个32位虚拟地址:0xABCD1234 。这个地址可以用二进制形式表示为:10101011 11001101 00010010 00110100

1. 虚拟地址结构分割

在32位系统中,虚拟地址通常被分为以下三个部分:

  • 页目录索引(Page Directory Index):前10位
  • 页表索引(Page Table Index):中间10位
  • 页内偏移量(Page Offset):最后12位

我们可以将0xABCD1234转换为二进制并分割这些部分:

  • 虚拟地址(Binary): 10101011 11001101 00010010 00110100
    • 页目录索引(前10位): 10101011 11
    • 页表索引(中间10位): 00110010 00
    • 页内偏移量(最后12位): 00110100

分割结果如下:

  • 页目录索引(PDI): 0xAB(即二进制10101011 11,十进制171)
  • 页表索引(PTI): 0x32(即二进制00110010 00,十进制50)
  • 页内偏移量(Offset): 0x34(即二进制00110100,十进制52)

2. 二级页表的操作流程

在二级页表机制中,虚拟地址的转换过程涉及两个主要阶段:页目录表和页表。以下是详细的操作流程:

2.1 页目录表的查找

  • 1.使用页目录索引:

    • 使用虚拟地址的前10位(0xAB)作为页目录索引,在页目录表中查找对应的页目录项(Page Directory Entry, PDE)。
    • 页目录表是一个数组,其中的每个项指向一个页表的物理地址。
  • 2.获取页目录项:

    • 页目录项包含了页表的物理地址。假设页目录项指向物理地址0x12345000。

2.2 页表的查找

  • 1. 使用页表索引:

    • 使用虚拟地址的中间10位(0x32)作为页表索引,在对应的页表中查找对应的页表项(Page Table Entry, PTE)。
    • 页表是一个数组,其中的每个项指向一个物理页框的地址。
  • 2. 获取页表项:

    • 页表项包含了物理页框的地址。假设页表项指向物理页框的起始地址0xABCDE000。

2.3 计算物理地址

  • 结合页内偏移量:

    • 将页表项中的物理页框地址(0xABCDE000 )与虚拟地址的最后12位 (0x34) 结合,得到最终的物理地址。
  • 物理地址的计算如下:

    • 物理地址 = 页表项的物理页框地址 + 页内偏移量
    • 物理地址 = 0xABCDE000 + 0x34 = 0xABCDE034

5.底层逻辑与组件

5.1 页目录项(PDE)和页表项(PTE)的组成

在32位系统中,页目录项 (Page Directory Entry, PDE)和页表项 (Page Table Entry, PTE)是管理虚拟内存的核心数据结构。它们的组成如下:

页目录项(PDE)数据结构:

cpp 复制代码
struct PageDirectoryEntry {
    uint32_t physicalPageFrameAddress : 20; // 20位,物理页框地址
    uint32_t reserved : 7;                // 预留位,通常不使用
    uint32_t writeThrough : 1;            // 写入通过位
    uint32_t cacheDisable : 1;            // 缓存禁用位
    uint32_t userSupervisor : 1;          // 用户/超级用户位
    uint32_t readWrite : 1;               // 读/写位
    uint32_t present : 1;                 // 存在位
};
  • 页目录项(PDE):

    • 物理页框地址:指向页表的物理地址。PDE的物理地址部分通常是20位,因此PDE中的这一部分包含了页表的起始地址。
    • 控制和状态位
      • 存在位(Present bit):标识页表是否在内存中。如果存在位为0,说明对应的页表不在内存中,访问该页时会产生缺页异常。
      • 读/写位(Read/Write bit):指示该页表是否可写。该位为1表示页表可写,为0则只读。
      • 用户/超级用户位(User/Supervisor bit):控制页表的访问权限。该位为1时,用户态程序可以访问,0时只能由操作系统访问。
      • 写入通过位(Write-through bit)(在某些系统中):控制是否使用写入通过缓存策略。
      • 缓存禁用位(Cache Disable bit)(在某些系统中):指示是否禁用缓存。

页表项(PTE)数据结构:

cpp 复制代码
struct PageTableEntry {
    uint32_t physicalPageFrameAddress : 20; // 20位,物理页框地址
    uint32_t reserved : 7;                // 预留位,通常不使用
    uint32_t writeThrough : 1;            // 写入通过位
    uint32_t cacheDisable : 1;            // 缓存禁用位
    uint32_t userSupervisor : 1;          // 用户/超级用户位
    uint32_t readWrite : 1;               // 读/写位
    uint32_t accessed : 1;                // 已访问位
    uint32_t dirty : 1;                   // 脏页位
    uint32_t present : 1;                 // 存在位
};
  • 页表项(PTE):

    • 物理页框地址:指向物理内存中的页框。PTE中的物理地址部分也是20位,包含了页面在物理内存中的起始地址。
    • 控制和状态位:
      • 存在位(Present bit):标识页框是否在物理内存中。如果存在位为0,说明该页框不在内存中,会导致缺页异常。
      • 读/写位(Read/Write bit):指示该页框是否可写。
      • 用户/超级用户位(User/Supervisor bit) :控制访问权限。
        已访问位(Accessed bit):指示该页框是否被访问过,用于页面置换算法。
      • 脏页位(Dirty bit):指示该页框是否被写过,主要用于页面置换时判断是否需要写回磁盘。

5.2 页目录和页表的大小计算

1. 页目录的大小

  • 结构:
    • 页目录包含1024个页目录项(PDE)。
    • 每个PDE的大小通常为4字节。
    • 页目录大小 = 1024 × 4字节 = 4096字节 = 4KB

2. 页表的大小

  • 结构:
    • 每个页表也包含1024个页表项(PTE)。
    • 每个PTE的大小通常为4字节。
    • 页表大小 = 1024 × 4字节 = 4096字节 = 4KB

3. 总的内存占用

为了计算系统内存的总占用量,我们需要考虑以下几点:

  1. 页目录表:
    • 只有一个页目录表,大小为4KB。
  2. 页表:
    • 每个页目录项(PDE)指向一个页表。
    • 页表的数量等于页目录中的项数,即1024个页表(每个页目录项指向一个页表)。
    • 每个页表的大小为4KB。、
    • 总的页表内存 = 1024 × 4KB = 4096KB = 4MB

总的内存占用4KB+4MB

为什么每个PTE的大小通常为4字节。


页表项(PTE)需要存储物理页框地址和控制和状态位

  • 物理页框地址
    在32位系统中,物理地址的高20位用于表示物理页框的地址。每个PTE中包含20位
    用于存储物理页框地址。
  • 控制和状态位 :每个PTE还需要包含控制和状态位,这些位的数量通常是12位
    (存在位、读/写位、用户/超级用户位、已访问位、脏页位等)。

由于PTE中物理地址部分占20位,加上控制和状态位共12位,总共需要32位(20位

地址 + 12位控制)。32位等于4字节(1字节 = 8位)。
为什么每个PDE的大小通常为4字节。


页表项(PTE)需要存储物理页框地址和控制和状态位

  • 物理页框地址
    32位系统中,页目录项用于存储页表的物理地址。物理地址部分也是20位。
  • 控制和状态位 :个PDE同样需要包含控制和状态位,这些位的数量通常是12位
    (存在位、读/写位、用户/超级用户位、写入通过位、缓存禁用位等)。

类似于PTE,PDE中物理地址部分占20位,加上控制和状态位共12位,总共也是32位(20位地址+12位控制)。32位等于4字节(1字节=8位)。

相关推荐
龙鸣丿41 分钟前
Linux基础学习笔记
linux·笔记·学习
耶啵奶膘2 小时前
uniapp-是否删除
linux·前端·uni-app
_.Switch3 小时前
高级Python自动化运维:容器安全与网络策略的深度解析
运维·网络·python·安全·自动化·devops
2401_850410833 小时前
文件系统和日志管理
linux·运维·服务器
JokerSZ.3 小时前
【基于LSM的ELF文件安全模块设计】参考
运维·网络·安全
XMYX-04 小时前
使用 SSH 蜜罐提升安全性和记录攻击活动
linux·ssh
芯盾时代4 小时前
数字身份发展趋势前瞻:身份韧性与安全
运维·安全·网络安全·密码学·信息与通信
心灵彼岸-诗和远方5 小时前
DevOps业务价值流:架构设计最佳实践
运维·产品经理·devops
一只哒布刘5 小时前
NFS服务器
运维·服务器
苹果醋36 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx