
本系列博客为复习操作系统导论的笔记,内容主要参考自:
目录
0x02 多级页表(Multi-level Page Tables)
0x05 三级页表(Three-level Page Table)
0x06 反向页表(Inverted Page Table)
我们现在来解决分页的第二个问题 —— 页表太大!
我们通常为系统中的每个进程设置一个页表,假设32位地址空间有4KB的页和4字节的页表项。

页表大小为:
Page table are too big and thus consume too much memory.
❓ 简单的解决方案:如果我们使用更大的页呢?
💡 假设32位地址空间有 16KB 页和 4 字节的页表项。

页表大小为: 
然而,这种方法的问题在于,大内存页会导致每页内的浪费!
因为浪费问题存在于分配单元的内部,所以这被称为 "内部碎片问题"(internal fragmentation),
因此,结果是应用程序回分配页,但只用每页的一小部分,而内存就会充满这些过大的页。
这未免有些太奢侈了哈,就像一道菜只吃两口就扔了一样,这很浪费的说……
Big pages lead to internal fragmentation.
Most of the page table is unused, full of invalid entries!

Page table for each segment
例子:每个进程有3个与之相关的页表

Address translation
硬件使用段位(SN)来决定使用哪一对基数和边界。
然后,硬件将页表的物理地址与VPN结合,如下所示形成页表的地址:
- 01: SN = (VirtualAddress & SEG_MASK) >> SN_SHIFT
- 02: VPN = (VirtualAddress & VPN_MASK) >> VPN_SHIFT
- 03: AddressOfPTE = Base[SN] + (VPN * sizeof(PTE))
遗憾的是,这种方法并非十全十美。首先,它仍然要使用分段。正如我们讨论的那样,分段并不像我们需要的那要灵活,因为他假定地址空间有一定的使用模式。例如,如果有一个大而稀疏的堆,仍然可能导致大量的页表浪费。其次,这种混合导致外部碎片再次出现。
来大哥了,既然前面介绍的两个都挺摆烂的,这个多级页表可能会让你眼前一亮。
❓ 思考:如何摆脱页表中所有这些无效的区域,而不是将它们全部保留在内存中?而不是把它们全部保留在内存中?
💡 把线性页表变成像树一样的玩意!

优点:
缺陷:

理解时空折中
在构建数据结构时,应始终考虑时间和空间的折中(time-space trade-off)。通常,如果你希望更快地访问特定的数据结构,就必须为该结构付出空间的代价。(正所谓以时间换空间)

项

Chop up the page table into page-sized units and create a page directory.
If an entire page of page-table entries is invalid, don’t allocate that page of the page table at all.

页目录(Page directory):
二级页表寻址(Addressing):

页目录索引(PDIndex)
它用于从VPN中找到页面目录条目(PDE)的地址。
PDEAddr = PageDirBase + (PDIndex * sizeof(PDE))
页表索引 (PTIndex)
它用于索引页表本身,给我们的 PTE 的地址。
PTEAddr = (PDE.PFN << SHIFT) + (PTIndex * sizeof(PTE))
PhysAddr = (PTE.PFN << SHIFT) + offset
二级页表 - 详细的内存视图:


Make each piece of the page table and page directory fit within a page !
❓ 一级两级三级……我们到底需要多少级?
单个页面中的 PTE 数量 (
) → 页表索引(7 bit)。
页目录索引需要 14 位(
个项目 -
个页)→ 太大!
页目录:
页目录中的条目 → 适合于一个页面内的项!
我们需要三级!

三级页表的例子:

多级页表控制流程 - TLB命中:
- VPN = (VirtualAddress & VPN_MASK) >> SHIFT
- (Success,TlbEntry) = TLB_Lookup(VPN)
-
- if ( Success == True ) // TLB Hit
- if ( CanAccess(TlbEntry.ProtectBits) == True )
- Offset = VirtualAddress & OFFSET_MASK
- PhysAddr = (TlbEntry.PFN << SHIFT) | Offset
- Register = AccessMemory(PhysAddr)
- else
- RaiseException(PROTECTION_FAULT);
- else
- // TLB Miss : perform the full multi-level lookup
多级页表控制流程 - TLB未命中:
- else // perform the full multi-level lookup
- PDIndex = (VPN & PD_MASK) >> PD_SHIFT
- PDEAddr = PDBR + (PDIndex * sizeof(PDE))
- PDE = AccessMemory(PDEAddr) // fetch PDE from PD
- if ( PDE.Valid == False ) RaiseException(SEGMENTATION_FAULT)
- else // PDE is Valid
- PTIndex = (VPN & PT_MASK) >> PT_SHIFT
- PTEAddr = (PDE.PFN << SHIFT) + (PTIndex * sizeof(PTE))
- PTE = AccessMemory(PTEAddr) // fetch PTE from PT
- if ( PTE.Valid == False )
- RaiseException(SEGMENTATION_FAULT)
- else if ( CanAccess(PTE.ProtectBits) == False )
- RaiseException(PROTECTION_FAULT);
- else // now refresh TLB and restart
- TLB_Insert(VPN, PTE.PFN, PTE.ProtectBits)
- RetryInstruction()
练习:

"生灵有倒悬之急"
One entry for each real page of memory.
每个表项由存储在该真实内存位置的页面的虚拟地址组成,并附有关于拥有该页面的进程的信息(即pid或asid)。
减少了存储每个页表所需的内存,但增加了在发生页引用时搜索表所需的时间。
使用哈希表将搜索限制在一个或最多几个(例如64位 UltraSPARC 和 PowerPC)。


- 📌 [ 笔者 ] 王亦优
- 📃 [ 更新 ] 2022.11.8
- ❌ [ 勘误 ] /* 暂无 */
- 📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,
- 本人也很想知道这些错误,恳望读者批评指正!
| 📜 参考资料 Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau, Operating Systems: Three Easy Pieces A. Silberschatz, P. Galvin, and G. Gagne, Operating System Concepts, 9th Edition, John Wiley & Sons, Inc., 2014, ISBN 978-1-118-09375-7. Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. . 百度百科[EB/OL]. []. https://baike.baidu.com/. |