四資工三 B10415037 陳品劭
Page tables在作業系統中,擔任了memory之間溝通的腳色,其儲存了虛擬位址到實體位址間的對映。一隻程式可能會使用一塊大的連續的memory,但實際上他們會分散在實體memory的各處。透過Page tables,使系統在執行程式時,可以更容易對memory進行操作。
P:是否陳列在 page table中。
W:是否能對此page執行write。
U:User 能否使用此page。
main呼叫 kvmalloc,create一個page table。kvmalloc呼叫setupkvm執行大部分的工作,先Allocate一塊記憶體給Page directory,然後呼叫mappages建立虛擬memory和實體memory的mapping。
mappages 呼叫 walkpgdir 找到對應的PTE,並初始化。walkpgdir透過address的前10bit找到page directory entry,若該page directory entry不存在,表示該page table page尚未被allocate。若存在,則會用接下來的10bits找到page table page的PTE。
void kvmalloc(void)
{
kpgdir = setupkvm();
switchkvm();
}
pde_t* setupkvm(void)
{
pde_t *pgdir;
struct kmap *k;
if((pgdir = (pde_t*)kalloc()) == 0) //allocate
return 0;
memset(pgdir, 0, PGSIZE); // 初始化 page directory
if (P2V(PHYSTOP) > (void*)DEVSPACE)
panic("PHYSTOP too high");
for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
if(mappages(pgdir, k−>virt, k−>phys_end − k−>phys_start,(uint)k−>phys_start, k−>perm) < 0)
//建立kernel需要的mapping
{
freevm(pgdir);
return 0;
}
return pgdir;
}
static int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm)
{
char *a, *last;
pte_t *pte;
a = (char*)PGROUNDDOWN((uint)va);
last = (char*)PGROUNDDOWN(((uint)va) + size − 1);
for(;;){
if((pte = walkpgdir(pgdir, a, 1)) == 0)
return −1;
if(*pte & PTE_P)
panic("remap");
*pte = pa | perm | PTE_P;
if(a == last)
break;
a += PGSIZE;
pa += PGSIZE;
}
return 0;
}
static pte_t * walkpgdir(pde_t *pgdir, const void *va, int alloc)
{
pde_t *pde;
pte_t *pgtab;
pde = &pgdir[PDX(va)];
if(*pde & PTE_P)
{
pgtab = (pte_t*)P2V(PTE_ADDR(*pde));
}
else
{
if(!alloc || (pgtab = (pte_t*)kalloc()) == 0)
return 0;
memset(pgtab, 0, PGSIZE);
*pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U;
}
return &pgtab[PTX(va)];
}
OS要控管資源,閒置的記憶體需要被OS收回,xv6用了一個Physical memory allocator來完成這件事。
main 呼叫kinit1、kinit2 初始化 allocator。kinit1 allocate 一塊
沒有被lock的4MB memory,kinit2 將lock 啟用,讓其他memory 可以被allocate。
kinit1 和 kinit2呼叫 freerange 將memory 加到free list,freerange 透過呼叫kfree 實現此功能。
p2v(PHYSTOP)將一個實體的address轉為一個虛擬的address。
kfree先將每個被release的Byte設為1,這樣能讓錯誤的程式在讀取已被釋放的memory時,盡快報錯。然後將v assign 給 r,r−>next = kmem.freelist,歸還memory。
void kinit1(void *vstart, void *vend)
{
initlock(&kmem.lock, "kmem");
kmem.use_lock = 0;
freerange(vstart, vend);
}
void kinit2(void *vstart, void *vend)
{
freerange(vstart, vend);
kmem.use_lock = 1;
}
void freerange(void *vstart, void *vend)
{
char *p;
p = (char*)PGROUNDUP((uint)vstart);
for(; p + PGSIZE <= (char*)vend; p += PGSIZE)
kfree(p);
}
void kfree(char *v)
{
struct run *r;
if((uint)v % PGSIZE || v < end || V2P(v) >= PHYSTOP)
panic("kfree");
memset(v, 1, PGSIZE);
if(kmem.use_lock)
acquire(&kmem.lock);
r = (struct run*)v;
r−>next = kmem.freelist;
kmem.freelist = r;
if(kmem.use_lock)
release(&kmem.lock);
}
system呼叫 growproc,若n>0,則allocate一或多塊memory並mapping到 process top 的空間。若n<0,則不會allocate,並釋放對應的page。
int growproc(int n)
{
uint sz;
struct proc *curproc = myproc();
sz = curproc−>sz;
if(n > 0)
{
if((sz = allocuvm(curproc−>pgdir, sz, sz + n)) == 0)
return −1;
}
else if(n < 0)
{
if((sz = deallocuvm(curproc−>pgdir, sz, sz + n)) == 0)
return −1;
}
curproc−>sz = sz;
switchuvm(curproc);
return 0;
}
int
deallocuvm(pde_t *pgdir, uint oldsz, uint newsz)
{
pte_t *pte;
uint a, pa;
if(newsz >= oldsz)
return oldsz;
a = PGROUNDUP(newsz);
for(; a < oldsz; a += PGSIZE){
pte = walkpgdir(pgdir, (char*)a, 0);
if(!pte)
a = PGADDR(PDX(a) + 1, 0, 0) − PGSIZE;
else if((*pte & PTE_P) != 0){
pa = PTE_ADDR(*pte);
if(pa == 0)
panic("kfree");
char *v = P2V(pa);
kfree(v);
*pte = 0;
}
}
return newsz;
}
int allocuvm(pde_t *pgdir, uint oldsz, uint newsz)
{
char *mem;
uint a;
if(newsz >= KERNBASE)
return 0;
if(newsz < oldsz)
return oldsz;
a = PGROUNDUP(oldsz);
for(; a < newsz; a += PGSIZE){
mem = kalloc();
if(mem == 0){
cprintf("allocuvm out of memory\n");
deallocuvm(pgdir, newsz, oldsz);
return 0;
}
memset(mem, 0, PGSIZE);
if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){
cprintf("allocuvm out of memory (2)\n");
deallocuvm(pgdir, newsz, oldsz);
kfree(mem);
return 0;
}
}
return newsz;
}