xv6手册:https://pdos.csail.mit.edu/6.S081/2020/xv6/book-riscv-rev1.pdf
相关翻译:http://xv6.dgs.zone/labs/requirements/lab5.html
感觉页表很多地方没理解,学习的时候把一些关键地方记录起来,如有错误恳请各位大佬指正。
页表笔记
页表是操作系统为每个进程提供私有地址空间和内存的机制。页表决定了内存地址的含义,以及物理内存的哪些部分可以访问。它们允许xv6隔离不同进程的地址空间,并将它们复用到单个物理内存上。页表还提供了一层抽象,这允许xv6执行一些特殊操作:映射相同的内存到不同的地址空间中,并用一个未映射的页面保护内核和用户栈区
页表基础
RISC-V指令(用户和内核指令)使用的是虚拟地址,而机器的RAM或物理内存是由物理地址索引的。RISC-V页表硬件通过将每个虚拟地址映射到物理地址来为这两种地址建立联系
XV6基于Sv39 RISC-V运行,这意味着它只使用64位虚拟地址的低39位,高25位不使用。虚拟地址的前27位用于索引页表,页表被逻辑上视作由 227 个页表条目(PTE)组成的数组,每个PTE中包含一个44位的物理页码(PPN),以及一些控制和状态的标志位。
地址转换过程:处理一个虚拟地址时,使用该虚拟地址的前27位在页表中查找对应的PTE。找到PTE后,PTE中的44位PPN与虚拟地址的低12位组合(即页内偏移),生成一个56位的物理地址
页表允许操作系统控制虚拟地址到物理地址的映射,并且以4096字节(4KB)的页为单位进行转换和管理,以 4096 ( 212 ) 字节的对齐块的粒度控制虚拟地址到物理地址的转换
实际的转换如下图所示:
在Sv39中,页表是一个三级树型结构,根页表是这棵树的根节点,它是一个4KB(4096字节)的页,每个页有512个PTE(每个PTE大小是8个字节(64位),总共可以有512个条目,4096字节 ÷ 8字节)。每个PTE记录了下一级页表的位置(也就是下一级页表的物理地址)。虚拟地址有39位,其中的前27位被用来在三级页表结构中进行查找。在找到第三级的PTE之后,PTE中会有物理页号(PPN),这个物理页号提供了物理地址的高44位,虚拟地址的最后12位作页内偏移。
根页表地址由myproc()->pagetable
读取
每一个页表的起始地址都是 4096 的倍数(页对齐要求)
每个进程都有独立的虚拟地址空间,Sv39模式下,使用64位虚拟地址的低39位,范围是从 0x0 到 0x7FFFFFFFFF,每个进程都可以拥有这一完整的虚拟地址范围。
页表缓存(Translation Lookaside Buffer,TLB)
对于一个虚拟内存地址的寻址,需要读三次内存,代价有点高。实际中,几乎所有的处理器都会对最近使用过的虚拟地址的翻译结果有缓存。这个缓存被称为:Translation Lookside Buffer,TLB。这就是Page Table Entry的缓存,也就是PTE的缓存
当处理器第一次查找一个虚拟地址时,硬件通过3级page table得到最终的PPN,TLB会保存虚拟地址到物理地址的映射关系。这样下一次访问同一个虚拟地址时,处理器可以查看TLB,TLB会直接返回物理地址,而不需要通过page table得到结果
如果切换了page table,操作系统需要告诉处理器当前正在切换page table,处理器会清空TLB。清空TLB的指令是sfence_vma
内核地址空间
右边地址0x1000是boot ROM的物理地址。对主板上电,主板做的第一件事情就是运行存储在boot ROM中的代码,当boot完成之后,会跳转到地址0x80000000,操作系统需要确保那个地址有一些数据能够接着启动操作系统
当机器刚刚启动时,还没有可用的page,XV6操作系统会设置好内核使用的虚拟地址空间,也就是这张图左边的地址分布
操作系统启动时,会从地址0x80000000开始运行
左侧低于PHYSTOP的虚拟地址,与右侧使用的物理地址是一样的(直接映射)
在完成了虚拟到物理地址的翻译之后,如果得到的物理地址大于0x80000000会走向DRAM芯片,如果得到的物理地址低于0x80000000会走向不同的I/O设备
kernel stack在虚拟内存中的地址很靠后,这是因为在它之下有一个未被映射的Guard page,这个Guard page对应的PTE的Valid 标志位没有设置,这样,如果kernel stack耗尽了,它会溢出到Guard page,但是因为Guard page的PTE中Valid标志位未设置,会导致立即触发page fault,这样的结果好过内存越界之后造成的数据混乱。立即触发一个panic(也就是page fault),就知道kernel stack出错了。同时又不想浪费物理内存给Guard page,所以Guard page不会映射到任何物理内存,它只是占据了虚拟地址空间的一段靠后的地址。同时,kernel stack被映射了两次,在靠后的虚拟地址映射了一次,在PHYSTOP下的Kernel data中又映射了一次,但是实际使用的时候用的是上面的部分,因为有Guard page会更加安全
Kernel text page被标位R-X,意味着你可以读它,也可以在这个地址段执行指令,但是你不能向Kernel text写数据。通过设置权限我们可以尽早的发现Bug从而避免Bug。对于Kernel data需要能被写入,所以它的标志位是RW-,但是你不能在这个地址段运行指令,所以它的X标志位未被设置。(所以,kernel text用来存代码,代码可以读,可以运行,但是不能篡改,kernel data用来存数据,数据可以读写,但是不能通过数据伪装代码在kernel中运行)
Free memory
:这段地址存放用户进程的page table(页表),text(代码段)和data(数据段)。如果运行了非常多的用户进程,某个时间点会耗尽这段内存,这个时候fork或者exec会返回错误。当kernel创建了一个进程,针对这个进程的page table也会从Free memory中分配出来。内核会为用户进程的page table分配几个page,并填入PTE。在某个时间点,当内核运行了这个进程,内核会将进程的根page table的地址加载到SATP中。从那个时间点开始,处理器会使用内核为那个进程构建的虚拟地址空间
标签:Mit6,PTE,虚拟地址,笔记,物理地址,地址,页表,S081,page From: https://www.cnblogs.com/Amroning/p/18528715