JOS虚拟页表的实现
JOS虚拟页表的实现
之前我们建立了基本的物理页,下面我们建立系统的虚拟地址系统,也就是二级页表。
对于所有对内存的访问,地址都被解释成虚拟地址,并由MMU 执行过程。程序员的工作是设置和维护页目录和页表。 实现虚拟页表的关键在于页目录,页表中存的是物理地址,而页目录项,页表项本身地址是虚拟地址。
pgdir_walk()这个函数的主要功能如下: ·根据给定页目录指针pgdir,返回线性地址va对应的页表项的指针。 ·如果相关页表还不存在,且create==false,返回NULL。 ·否则,通过page_alloc为页表分配一页物理内存,并相应初始化,清0。返回va对应的表项的指针。
pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create)
{
// Fill this function in
pde_t *pgdir_entry = &pgdir[PDX(va)];//页目录获取页目录项,里面存的是物理地址
pte_t *pgtable;//虚拟地址
struct PageInfo *pp;
if(*pgdir_entry & PTE_P)
{
pgtable = (pte_t*)KADDR(PTE_ADDR(*pgdir_entry));//如果存在的话,物理地址转化为虚拟地址
}
else
{
if(create ==false)
return NULL;
if((pp = page_alloc(ALLOC_ZERO)) ==0)
return NULL;
pp->pp_ref = 1;
pgtable = (pte_t*)KADDR(page2pa(pp));//将页的虚拟地址转化为物理地址
*pgdir_entry = PADDR(pgtable) | PTE_P |PTE_W |PTE_U;
}
return &pgtable[PTX(va)];
}
boot_map_region()这个函数的主要功能如下: ·根据指定页目录pgdir,权限perm,页大小PGSIZE,将[va, va+size)的虚拟地址映射到物理地址的[pa, pa+size)。
static void
boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
{
// Fill this function in
uintptr_t offset;
pte_t *pte;
for(offset =0 ;offset < size ;offset+=PGSIZE)
{
pte = pgdir_walk(pgdir,(char *)va,1);
if(!pte)
panic("boot_map_region:pgdir_walk failed");
if(*pte & PTE_P)
panic("boot_map_region:pte remap");
*pte = pa | perm |PTE_P;
va +=PGSIZE;
pa +=PGSIZE;
}
}
下面我来说一下JOS的内存分布图,默认是低地址留给用户空间,高地址留给kernel,从0到ULIM为用户空间,从ULIM到4GB为kernel address。 另外需要对每一块规定权限,地址空间大致被分为三个部分:[0, UTOP)是用户可以读写的;[UTOP,ULIM)对用户和内核都是只读的,这部分地址的作用是向用户暴露一部分内核的数据内容;ULIM以上的地址,用户没有任何权限。
另外kernelstack是从高向低地址生长的,[KSTACKTOP-KSTKSIZE, KSTACKTOP)是真正会使用到的堆栈空间;而[KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE)这部分,是为了防止栈溢出时,不小心修改到紧接着的地址空间中的数据,也就是说这是一个”guard page”,即缓冲区。_这体现了JOS考虑问题的保守性和稳健性。所以[KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE)中的虚拟地址仅仅是保留的,不需要映射。
我们将KERNBASE开始映射到整个物理地址空间。这样做的目的,是为了稍后打开页式转换以后,Kernel也能使用统一的虚拟地址来访问其自身数据,即Kernel使用虚拟地址KERNBASE+x访问到的物理地址,正是物理地址为x处的内存。
/*
* Virtual memory map: Permissions
* kernel/user
*
* 4 Gig --------> +------------------------------+
* | | RW/--
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* : . :
* : . :
* : . :
* |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| RW/--
* | | RW/--
* | Remapped Physical Memory | RW/--
* | | RW/--
* KERNBASE, ----> +------------------------------+ 0xf0000000 --+
* KSTACKTOP | CPU0's Kernel Stack | RW/-- KSTKSIZE |
* | - - - - - - - - - - - - - - -| |
* | Invalid Memory (*) | --/-- KSTKGAP |
* +------------------------------+ |
* | CPU1's Kernel Stack | RW/-- KSTKSIZE |
* | - - - - - - - - - - - - - - -| PTSIZE
* | Invalid Memory (*) | --/-- KSTKGAP |
* +------------------------------+ |
* : . : |
* : . : |
* MMIOLIM ------> +------------------------------+ 0xefc00000 --+
* | Memory-mapped I/O | RW/-- PTSIZE
* ULIM, MMIOBASE --> +------------------------------+ 0xef800000
* | Cur. Page Table (User R-) | R-/R- PTSIZE
* UVPT ----> +------------------------------+ 0xef400000
* | RO PAGES | R-/R- PTSIZE
* UPAGES ----> +------------------------------+ 0xef000000
* | RO ENVS | R-/R- PTSIZE
* UTOP,UENVS ------> +------------------------------+ 0xeec00000
* UXSTACKTOP -/ | User Exception Stack | RW/RW PGSIZE
* +------------------------------+ 0xeebff000
* | Empty Memory (*) | --/-- PGSIZE
* USTACKTOP ---> +------------------------------+ 0xeebfe000
* | Normal User Stack | RW/RW PGSIZE
* +------------------------------+ 0xeebfd000
* | |
* | |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* . .
* . .
* . .
* |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
* | Program Data & Heap |
* UTEXT --------> +------------------------------+ 0x00800000
* PFTEMP -------> | Empty Memory (*) | PTSIZE
* | |
* UTEMP --------> +------------------------------+ 0x00400000 --+
* | Empty Memory (*) | |
* | - - - - - - - - - - - - - - -| |
* | User STAB Data (optional) | PTSIZE
* USTABDATA ----> +------------------------------+ 0x00200000 |
* | Empty Memory (*) | |
* 0 ------------> +------------------------------+ --+
*
* (*) Note: The kernel ensures that "Invalid Memory" is *never* mapped.
* "Empty Memory" is normally unmapped, but user programs may map pages
* there if desired. JOS user programs map pages temporarily at UTEMP.
*/