分页存储管理
分页存储管理
复习定位
连续分配的外碎片问题让大程序无法装入紧挨的连续空间——尽管总空闲空间足够。分页把逻辑地址空间和物理内存切成固定的等大小"页"——逻辑页可以放到物理内存的任意空闲"页框"(物理上的框架)中。比连续分配更灵活——没有外碎片。但每次内存访问从一次变成两次(页表查表和数据访问)——TLB作为Cache解决了这个代价。
分页的基本原理
逻辑地址空间被划分为固定大小的页(通常4KB)。物理内存被划分为同样大小的页框(page frame, 4KB)。进程的逻辑页可以装入任意空闲的物理页框中——操作系统通过页表记录每个逻辑页号对应的物理页框号。
逻辑地址转换为物理地址的公式:
页号=逻辑地址/页大小(例如4KB)
页内偏移=逻辑地址%页大小
物理地址=页表[页号]×页大小+页内偏移例如逻辑地址0x3A 在4KB(0x1000)页系统中——页号=0(0x0~0xFFF)、偏移=0x3A——如果页表[0]=0x7——则物理地址=0x7000+0x3A=0x703A。
每次访问内存地址时需要经过两次内存访问:第一次查页表得到物理页框号——第二次访问真实物理地址。这让性能损失一半——所以TLB快表用于缓存最近使用的页表项——TLB命中时直接得到物理页框号——只访问一次内存。
页表结构
一级页表——页表是线性数组——页号作为下标。32位逻辑地址(页大小4KB, 220页)需要220约100万个页表项——每个页表项4字节→页表大小4MB。每个进程都需要一个4MB的页表——对内存是浪费。
两级页表——将页表自身也分页。32位地址分为:10位页目录索引、10位页表索引、12位页内偏移。页目录大小=2^10×4=4KB、每张页表=4KB——如果进程只用到了4MB地址空间——则只使用1张页表+1个页目录=8KB而不是4MB。这就是多级页表节省内存的关键:不为不使用的地址空间部分建页表。
64位系统的挑战——48位虚拟地址空间用四级页表(PGD→PUD→PMD→PTE)每级索引9位+12位偏移。五级页表(57位地址)在近年也被加入。页表层级越多——地址转换的硬件查表路径(页表遍历)越长——硬件MMU的查找次数越多——但减少了页表本身占用的目录项内存。
TLB快表
TLB是CPU内部的高速硬件缓存——缓存最近使用的页表项。TLB条目包含:页号+页框号+保护位+有效位。CPU在地址转换时先并行查询TLB(内容寻址CAM)而不经页表遍历:
- TLB命中(<1ns)→直接得到物理地址——无需查内存页表
- TLB未命中(→100ns)→查内存中的多级页表——更新TLB并返回物理地址
上下文切换(从一个进程切换到另一个进程)时——需要刷新TLB(因为不同进程的页表映射完全不同)或使用地址空间标识符(ASID)标记每个TLB条目的进程归属以避免刷全新切换。
页表项的保护位
每个页表项(PTE)含控制位:
- 存在位(Present): 该页是否在物理内存中——不在则触发缺页中断
- 读写位(R/W): 该页是否可写——0=只读——可用于写时复制(COW)
- 用户/超级用户位(U/S): 该页是否允许用户态访问——0=仅内核态可访问
- 脏位(Dirty): 该页自从装入后是否被写过——置换时是否需要写回磁盘
- 访问位(Accessed): 该页最近是否被访问过——用于置换算法选择非活跃页
复习检查
逻辑地址0x1234在4KB(0x1000)的页系统中——页号和页内偏移分别是什么?如果页表映射页号0→页框5——物理地址是多少?
两级页表将页表本身也放入页框——如何节省了为未使用地址空间留下页目录的空间?用4MB全页表和两层4MB页目录8KB的差异进一步解释。
TLB在进程切换时的处理——不刷新TLB会怎样?ASID如何避免刷新?
页表项的脏位(Dirty)有什么意义——为什么页面置换算法优先置换未被修改(不dirty)的页?
为什么分页消除了外碎片——但会不会有内部碎片?每个进程的最后一项可能没有填满整个页框——这属于内部碎片还是外部碎片?