寄存器satp
操作系统分为用户模式和内核模式,riscv架构也分为特权架构和非特权架构。特权架构指的是因为定时器中断,异常和系统调用等情况,进程从用户模式切换到内核模式时,对一些特权架构的寄存器进行一系列的操作,也是用户态和内核态可以进行分离的实现原理。
Satp(Supervisor Address Translation and Protection Register)
寄存器是虚拟地址转换的一个非常重要的寄存器,下图展示了stap寄存器的内容。
当MODE位为0时,虚拟地址不会进行转化,此刻的虚拟地址就是物理地址。软件会将satp寄存器的其他字段清0。MODE为不同值时,会根据MODE位选择不同结构的页表,如下图所示。在32位的操作系统下,页表只有sv32一种结构。在64位操作系统下,有sv39,sv48等多种页表结构。
PPN存放的是最高级页表的起始页号(页目录),根据相应的page大小进行可以得到最高级页表(页目录)的物理地址。这样cpu就可以告诉虚拟内存地址从哪里翻译成物理内存地址。(页表的地址必须为物理地址,因为内容本身只能存放在真正的物理地址中,页表是虚拟地址指向物理地址的媒介)每一个应用程序都有属于自己的页表,当cpu从一个应用程序切换到另一个应用程序的时候,也需要切换satp寄存器中的内容。每个进程都有自己的进程描述符,在进程描述符中会定义页目录的地址并将该地址写入到stap寄存器中。
页表和多级页表
如果直接将虚拟地址一一对应映射到物理地址,那么对于页表机制的空间需求太大了。
比如全中国14亿人,如果每个人的信息都是中国浙江嘉兴海宁奕斯伟xxx,那么14亿人占据的信息大小就会非常大。而如果对信息进行分类,比如同属中国,保留34个省,再保留下面的县,那么需要保存的数据量将会变得非常小(类似于填写快递收货地址)。页表的分页机制就是类似这种原理。
一级页表的思路是:定位数据所在的页和页内的偏移,就可以转化成为物理地址。如下图:
3212位共计20位元素,可以表示2^20=1M个页,一个页的大小为4k,可以正好覆盖虚拟内存的1M*4K=4GB。011位共计12位可以覆盖页的大小:2^12=4k,可以表示为页内偏移。该方法需要为页表分配1M(页表数量)*4k(单个页表的大小)=4MB的内存空间。占用空间还是太大,于是多级页表出现了。
虚拟地址和物理地址的组成
先来看虚拟地址的组成:
再来看转换后的物理地址:
页表要做的事情就是将虚拟地址中的20位的VPN[0]和VPN[1]经过页表的转换,变成22位的PPN[0]和PPN[1],最后的12位page offset称作页内偏移,物理地址和虚拟地址的page offset是一样的。转换的形式如下图所示:
页表项PTE
页表项(page table entry)是页表中存储的内容,是寻址的媒介与核心。sv32中,页表包含了2^10个PTEs,每个页表项为4个字节。下图展示了PTE的组成。
V位表示PTE是否有效,如果V=0,则PTE中的其他位是无效的。R,W,X分别表示页的可读,可写,可执行权限。当这三位都为0时,PTE中的PPN表示的是指向下一级页表的物理页号。下图表示的是,XWR在不同的权限位时,PTE的含义。
U位表示的是当前页在用户模式下是否可用。只有当U=1时,用户模式下才可以使用当前页。当sstatus寄存器的SUM位为1时,表明supervisor模式下可以访问user模式下的页面。当SUM位为1,且U=1时,supervisor模式下的softwatre可以访问页面。但是通常情况下,SUM为为0,supervisor访问user模式下的页会产生错误,而且SUM位是否位1,,supervisor模式尽量不要访问U=1的页面。
G位表示是否是全局映射。未能将全局映射设置为全局的,会降低性能。而将非全局映射标记会全局的,可能会导致bug。
RSW保留供supervisor模式下使用,此处可以暂时忽略。
每个lead PTE(即表示虚拟地址对应物理页号的PTE)都包含A(access)和D(dirty)位。其中A位表示虚拟页在上次A被清零之后是否被读/写/执行过。D位表示虚拟页在D位上次被清零之后是否被写过。正常情况下将A和D位置1来提高效率。对于non-leaf PTE,D,A,U位被保留用作未来的标准使用,并且必须被软件清零。
多级页表寻址过程
Sv32虚拟地址va被转化成物理地址pa的过程如下:
-
a = stap.ppn * PAGESIZE, i = LEVELS - 1
(stap中的ppn字段表示的页目录的物理页号,乘PAGESIZE得到的是页目录的物理地址。对于sv32,PAGESIZE = 2^12(4Kb), LEVELS = 2,表示的是二级页表映射,i表示VPN[1], VPN[0],所以要减1) - pte的地址:
a + va.vpn[i]*PTESIZE
(a是页目录的物理地址,vpn[i]中存放的是index,乘PTESIZE得到了对应PTE的偏移地址,加上页目录的基地址之后就得到了页目录对应的PTE,可以通过PTE得到下一级页表的)
此时需要对pte的进行安全性的检查,如果违反PMA和PMP的检查,则出发一个access-fault - 如果pte的V位为0,或者r为0且w位为1,则停止并原始的
access type
触发一个page fault - 如果经过了上述的检查,则表明PTE是有效的。如果pte页表项的r为1,或者x为1,则直接跳转到步骤5。(表明此时PTE为
leaf PTE
,已经寻找到了对应虚拟地址对应的物理页号)。否则PTE中的PPN指向的是下一级页表的物理页号。将i = i-1,如果i=0,则停止并根据原始的access type触发一个page fault。如果i大于等于0,a = pte.ppn * PAGESIZE(得到了下一级页表的物理页号*size得到了下一级页表的物理地址),并跳转到步骤2。 - 到第五步说明已经寻找到了
leaf PTE
。根据当前特权模式以及mstatus寄存器中的SUM和MXR(如果MXR = 0,只有load被标记位可读的页才可以成功,如果为1,则load可读可执行的页都会成功)字段的值,判断pte中的r,w,x和u位是否符合内存访问的请求。如果不符合,则停止并根据原始的access type触发一个page fault。 - 如果i>0,且pte.ppn[i-1 : 0] 不等于0,则说明这是一个未对齐的
super page
,停止并根据原始的access type触发一个page fault - 如果pte的a位为0,或者memory access为store且pte的d位为0时,根据原始access type触发一个page fault,或者:
- 将pte的a位置1,且如果memory access是store时,将pte的d位设为1
- 如果access违反了PMA或者PMP的检查,触发一个page fault
- 此更新和步骤2中load pte的过程必须是原子的,尤其地,不能在此过程中插入store操作
- 到该步骤则说明虚拟地址到物理地址的转换是成功的。leaf PTE中的PPN就是最终转换成功的物理页号,加上虚拟地址的offset就变成了物理地址。转换后的物理地址要满足一下特性:
-
pa.pgoff = va.pgof
(物理地址的页内偏移与虚拟地址的页内偏移是一样的) - 如果i>0,则说明这是一个superpage的转换,pa.ppn[i-1 : 0] = va.vpn[i-1 : 0]。
-
pa.ppn[LEVELS-1 :i] = pte.ppn[LEVELS-1 : i]
(最终的物理地址的物理页号是leaf PTE的物理页号)
64位操作系统的多级页表转换
32位操作系统的页表转换只有sv32一种,已经在2.3.4中详细讲述。64位操作系统根据satp寄存器的MODE位可以分为sv39和sv48等多种页表转换方式,但原理其实和sv32差不多。sv39采用的是三级页表,sv48为四级页表,下图分别展示了在sv39和sv48时的虚拟地址,物理地址和页表项pte。
下图来自MIT的xv6操作系统课程对sv39三级页表映射方案从虚拟地址到物理地址地址转换的总结。
Page fault
产生page fault的情况有很多种,比如因为懒加载机制,并没有给当前虚拟地址分配物理地址;根据地址转换的安全性检查,也会产生page fault;或者pte因为物理存储机制被存放至磁盘空间,也需要page fault进行swap等等。本章节将会讲述产生page fault的原因,后续章节中会讲述xvisor是如何具体处理page fault的。下图展示了在地址转换的过程中出现page fault的情形:
内核空间的处理比较简单,也不容易出现page fault的情况,只要处理vmalloc即可。对于用户空间来说,page fault的处理要考虑的情况比较多。首先要对虚拟地址的合法性进行检查。如果一个地址不在合法的VMA区间内,就判定为bad area,并处罚segmentation fault。如果在地址合法的情况下,首先考虑是否是因为用户控件的malloc懒加载机制。
因为malloc是动态分配内存机制,并且为了节省内存,内核并不会立刻为其分配物理内存,而是只是对vma进行信息记录。当地址真正被使用到的时候,处罚page fault,通过mmap建立对应的heap和stack内存区域的映射。在x86的实现机制中,还有一种情况(riscv的linux需要考证),就是在pte页表项的P位为0时,表明该页表项是存在的。只是从内存空间拷贝至了外部磁盘空间,需要调用swap_page将页面的内容拷贝回内存。
参考资料:
MIT操作系统课程
兰新宇:linux内核和虚拟化博客
riscv特权架构文档
微信读书:qemu/kvm源码解析与应用
《系统虚拟化》