深入理解linux内存管理之 页表管理

页表是内存管理系统中的数据结构,用于向每个进程提供一致的虚拟地址空间,每个页表项保存的是虚拟地址到物理地址的映射以及一些管理标志。应用进程只能访问虚拟地址,内核必须借助页表和硬件把虚拟地址翻译为对物理地址的访问。

页表作用

在使用虚拟地址空间的linux操作系统上,每一个进程都工作在一个4G的地址空间上,其中0~3G是应用进程可以访问的user地址空间,是这个进程独有的,其他进程看不到也无法操作这个地址空间;3G~4G是kernel地址空间,所有进程共享这部分地址空间。

由于每个进程都有3G的私有进程空间,所以系统的物理内存无法对这些地址空间进行一一映射,因此kernel需要一种机制,把进程地址空间映射到物理内存上。当一个进程请求访问内存时,操作系统通过存储在kernel中的进程页表把这个虚拟地址映射到物理地址,如果还没有为这个地址建立页表项,那么操作系统就为这个访问的地址建立页表项。最基本的映射单位是page,对应的是页表项PTE

页表项和物理地址是多对一的关系,即多个页表项可以对应一个物理页面,因而支持共享内存的实现(几个进程同时共享物理内存)。

页表的实现

实现虚拟地址到物理地址转换最容易想到的方法是使用数组,对虚拟地址空间的每一个页,都分配一个数组项。但是有一个问题,考虑IA32体系结构下,页面大小为4KB,整个虚拟地址空间为4GB,则需要包含1M个页表项,这还只是一个进程,因为每个进程都有自己独立的页表。因此,系统所有的内存都来存放页表项恐怕都不够。

相像一下进程的虚拟地址空间,实际上大部分是空闲的,真正映射的区域几乎是汪洋大海中的小岛,因次我们可以考虑使用多级页表,可以减少页表内存使用量。实际上多级页表也是各种体系结构支持的,没有硬件支持,我们是没有办法实现页表转换的。

为了减少页表的大小并忽略未做实际映射的区域,计算机体系结构的设计都会靠虑将虚拟地址划分为多个部分。具体的体系结构划分方式不同,比如ARM7和IA32就有不同的划分,在这里我们不讨论这部分内容。

Linux操作系统使用4级页表


图中CR3保存着进程页目录PGD的地址,不同的进程有不同的页目录地址。进程切换时,操作系统负责把页目录地址装入CR3寄存器。

地址翻译过程如下

1. 对于给定的线性地址,根据线性地址的bit22~bit31作为页目录项索引值,在CR3所指向的页目录中找到一个页目录项。

2. 找到的页目录项对应着页表,根据线性地址的bit12~bit21作为页表项索引值,在页表中找到一个页表项。

3. 找到的页表项中包含着一个页面的地址,线性地址的bit0~bit11作为页内偏移值和找到的页确定线性地址对应的物理地址。

这个地址翻译过程完全是由硬件完成的


页表转化失败

在地址转换过程中,有两种情况会导致失败发生。

1. 第一 要访问的地址不存在,这通常意味着由于编程错误访问了无效的虚拟地址,操作系统必须采取某种措施来处理这种情况,对于现代操作系统,发送一个段错误给程序;或者要访问的页面还没有被映射进来,此时操作系统要为这个线性地址分配相应的物理页面,并更新页表。

2. 第二 要查找的页不在物理内存中,比如页已经交换出物理内存。在这种情况下需要把页从磁盘交换回物理内存。


TLB

CPU的Memory management unit(MMU)cache了最近使用的页面映射。我们称之为translation lookaside buffer(TLB)。TLB是一个组相连的cache。当一个虚拟地址需要转换成物理地址时,首先搜索TLB。如果发现了匹配(TLB命中),那么直接返回物理地址并访问。然而,如果没有匹配项(TLB miss),那么就要从页表中查找匹配项,如果存在也要把结果写回 TLB。


页表格式

页目录项和页表项大小都是32bit(4 bytes),由于4KB地址对齐的原因,页目录项和页表项只有bit12~bit31用于地址,剩余的低12bits则用来描述页有关的附加信息。尽管这些位是特定于CPU的,下列位在linux 内核支持的大部分CPU都能找到:

Present

页目录项和页表项都包含这个位

虚拟地址对应的物理页面不在内存中,比如页被交换出去,此时页表项的其他部分通常会代表不同的含义,因为不需要描述页在物理内存中的地址,相反,需要信息来找到换出的页。

如果页目录或者页表项的Present位为0, 那么CPU分页单元把虚拟地址存储到CR2中,然后生成一个异常14:page fault异常。

Accessed

每次分页单元访问页面时,都会自动设置Accessed位,内核会定期检查该位,以便确定页的活跃程度,内核会选择不活跃的页面swapout到交换空间。注意分页单元只负责置位,清除位操作要内核自己执行。

Dirty

仅仅存在于页表项,每当向页帧写入数据分页单元都会设置dirty标志,swap进程可以通过这个位来决定是否选择这个页面进行交换。记住,分页单元不会清除这个标记,所以必须由操作系统来清除这个标记。

Read/Write

包含了页面的读写权限,如果设置为0,那么只有读权限;设置为1,则有读写权限。

User/Supervisor

User允许用户空间代码访问该页;Supervisor只有内核才可以访问。

Exec

在较新的64bit处理器上,分页单元支持No eXec位,因此2.6.11内核开始加入了这个标志。


页表项的创建和操作

所有体系结构都要实现下面的页表项创建,释放和操作函数,以便于内存管理代码创建和销毁页表

函数描述
mk_pte创建一个页表项,必须将page实列和所需的访问权限作为参数传入
pte_page获得页表项描述的页对应的page实列地址
pgd_alloc分配并初始化可容纳一个完整目录表的内存(不是一个表项)
pud_alloc 
pmd_alloc 
pte_alloc 
pgd_free释放目录表占用的内存
pud_free 
pmd_free 
pte_free 
set_pgd设置页目录项中某项的值
set_pud 
set_pmd 
set_pte 



  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值