上一篇文章讲了地址翻译 的一些基本概念,这一次记录些容易出现理解错误的地方。
1. TLB 是硬件实现的
TLB(translation lookaside buffer)存在于MMU中,是由硬件实现的,通过一定的机制,讲TLBT于TLBI一个set中的所有tag同时进行比较。
2. PTE存储的值是物理地址
在很多时候,由于虚拟地址和物理地址都是同样的word size(比如32),我们常常忽略掉PTE中存储的地址究竟是虚拟地址还是物理地址。答案是物理地址。关于这一点,我们可以这样思考——如果存储的是虚拟地址,那么OS在翻译一个虚拟地址时,通过VPN1,CR3找到一个PTE,通过对PTE取值,得到下一节页表,如果是个虚拟地址,则又要对它进行翻译……于是进入了一个死循环(常见递归错误)。
3. Page的基地址、CR3(PTBR)都是物理地址
同2中所说,PTE中存放的是物理地址。通过第一级页表中的PTE找到的是下一级页表的基地址,那么页表的基地址(包括CR3)都是物理地址
4. PTE中的最后几位为权限位
在ICS这门课程中,我们经常会练习的题目是给出一定的VPN和value的对应值,以及一个特殊说明valid or invalid. 这个valid位就是存在于PTE值的最后一位。我们可以在JOS的lab中看到,当OS要翻译一个虚拟地址时,每当通过PTE得到其对应的值都要执行
if (!( value & PTE_P )) return ~0x0;
其中PTE_P的值位0x1,即检查其最后一位判断这个page及其子page是否有效。
5. VPN只是PTE个数的的offset
同在第4点中说的原因一样,在得到VPN后常常会出现直接将CR3的值(当作size_t)与VPN相加的错误。我们在JOS的lab中可以看到一个二级页表的地址翻译可以如下实现:
// This function returns the physical address of the page containing 'va', // defined by the page directory 'pgdir'. The hardware normally performs // this functionality for us! We define our own version to help check // the check_kern_pgdir() function; static physaddr_t check_va2pa(pde_t *pgdir, uintptr_t va) { pte_t *p; pgdir = &pgdir[PDX(va)]; if (!(*pgdir & PTE_P)) return ~0; p = (pte_t*) KADDR(PTE_ADDR(*pgdir)); if (!(p[PTX(va)] & PTE_P)) return ~0; return PTE_ADDR(p[PTX(va)]); }
pgdir在这里指代的就是CR3,而&pgdir[PDX(va)] == pgdir + PDX(va). 注意在这里pgdir是一个指针,也就是说,应该将VPN乘以4再加上CR3的值。比如CR3是0x40000,VPN是0x101,那么PTE应该是0x40404.