組長:B10415011、賴君庭
組員:B10415024、廖珮均
github: https://github.com/Nina9721/xv6-public/
系統流程圖

user使用流程(test1)

例外狀況:
address是virtual address的illege range(意即他無法process的page table),印出錯誤訊息,且刪掉這個讓page fault產生的process

test1(cow vs fork)

test2

test3

有做更改的檔案

defs.h
char* kalloc(void);
void kfree(char*);
void kinit1(void*, void*);
void kinit2(void*, void*);
uint getNumFreePages(void);//B10415011
uint getRefcount(uint pa);//B10415011
void decrementRefcount(uint pa);//B10415011
void incrementRefcount(uint pa);//B10415011
// vm.c
void seginit(void);
void kvmalloc(void);
pde_t* setupkvm(void);
char* uva2ka(pde_t*, char*);
int allocuvm(pde_t*, uint, uint);
int deallocuvm(pde_t*, uint, uint);
void freevm(pde_t*);
void inituvm(pde_t*, char*, uint);
int loaduvm(pde_t*, char*, struct inode*, uint, uint);
pde_t* copyuvm(pde_t*, uint);
void switchuvm(struct proc*);
void switchkvm(void);
int copyout(pde_t*, uint, void*, uint);
void clearpteu(pde_t *pgdir, char *uva);
void pagefault(uint err_code);//B10415011
usys.S
SYSCALL(getNumFreePages)//B10415011
Makefile
//B10415011
_testcow\
syscall.c
extern int sys_getNumFreePages(void);//B10415011
[SYS_getNumFreePages] sys_getNumFreePages,//B10415011
syscall.h
#define SYS_getNumFreePages 22//B10415011
sysproc.c
//B10415011
int
sys_getNumFreePages(void)
{
return getNumFreePages();
}
user.h
int getNumFreePages(void);//B10415011
vm.c
pde_t*
copyuvm(pde_t *pgdir, uint sz)
{
pde_t *d;
pte_t *pte;
uint pa, i, flags;
//char *mem;
if((d = setupkvm()) == 0)
return 0;
for(i = 0; i < sz; i += PGSIZE){
if((pte = walkpgdir(pgdir, (void *) i, 0)) == 0)
panic("copyuvm: pte should exist");
if(!(*pte & PTE_P))
panic("copyuvm: page not present");
// B10415011
// Read only
*pte &= ~PTE_W;
pa = PTE_ADDR(*pte);
flags = PTE_FLAGS(*pte);
//if((mem = kalloc()) == 0)
// goto bad;
//memmove(mem, (char*)P2V(pa), PGSIZE);
//if(mappages(d, (void*)i, PGSIZE, V2P(mem), flags) < 0)
if(mappages(d, (void*)i, PGSIZE, pa, flags) < 0)
goto bad;
incrementRefcount(pa);
}
// Flush TLB for original process
lcr3(V2P(pgdir));
return d;
bad:
freevm(d);
// Flush TLB
// Some entries in the original process page table have been changed
lcr3(V2P(pgdir));
return 0;
}
//B10415024
void
pagefault(uint err_code)
{
//cprintf("Page fault occured.\n");
//struct proc *curproc = myproc();
//struct cpu *curcpu = mycpu();//cpu->apicid
// Get the fault virtual address from the CR2 register
uint va = rcr2();
pte_t *pte;
// Error handling code
if(myproc() == 0)
{
cprintf("Page fault with no user process from cpu %d, cr2=0x%x\n", cpuid(), va);
panic("pagefault");
}
if(va >= KERNBASE || (pte = walkpgdir(myproc()->pgdir, (void*)va, 0)) == 0 || !(*pte & PTE_P) || !(*pte & PTE_U))
{
cprintf("Illegal virtual address on cpu %d address 0x%x, kill proc %s with pid %d\n", cpuid(), va, myproc()->name, myproc()->pid);
// Mark the process as killed
myproc()->killed = 1;
return ;
}
// Current page has write permissions enabled
if(*pte & PTE_W)
{
cprintf("Error code: %x, address 0x%x\n", err_code, va);
panic("Page fault already writeable");
}
// Get the physical address from the given page table entry
uint pa = PTE_ADDR(*pte);
// Get the refcount of the current page
uint refcount = getRefcount(pa);
char *mem;
// Current process is the first one that tries to write to this page
if(refcount > 1)
{
// Allocate a new memory page for the process
if((mem = kalloc()) == 0)
{
cprintf("Page fault out of memory, kill proc %s with pid %d\n", myproc()->name, myproc()->pid);
myproc()->killed = 1;
return;
}
// Copy the contents from the original memory page pointed the virtual address
memmove(mem, (char*)P2V(pa), PGSIZE);
// Point the given page table entry to the new page
*pte = V2P(mem) | PTE_P | PTE_U | PTE_W;
// Since the current process now doesn't point to original page
decrementRefcount(pa);
}
// Current process is the last one that tries to write to this page
else if(refcount == 1)
{
// Remove the read only restriction on the trapping page
*pte |= PTE_W;
}
else
{
panic("pagefault reference count wrong\n");
}
// Flush TLB for process
lcr3(V2P(myproc()->pgdir));
}
kalloc.c
struct {
struct spinlock lock;
int use_lock;
struct run *freelist;
uint numFreePages;//B10415024// store the numFreePages
uint pg_refcount[PHYSTOP >> PGSHIFT];//B10415024
} kmem;
void
kinit1(void *vstart, void *vend)
{
initlock(&kmem.lock, "kmem");
kmem.use_lock = 0;
//B10415024
// Initialize the numFreePages
kmem.numFreePages = 0;
freerange(vstart, vend);
}
void
freerange(void *vstart, void *vend)
{
char *p;
p = (char*)PGROUNDUP((uint)vstart);
for(; p + PGSIZE <= (char*)vend; p += PGSIZE)
{
//B10415024
// Initialize the refcount
kmem.pg_refcount[V2P(p) >> PGSHIFT] = 0;
kfree(p);
}
}
void
kfree(char *v)
{
struct run *r;
if((uint)v % PGSIZE || v < end || V2P(v) >= PHYSTOP)
panic("kfree");
if(kmem.use_lock)
acquire(&kmem.lock);
r = (struct run*)v;
//B10415024
// Someone free it
if(kmem.pg_refcount[V2P(v) >> PGSHIFT] > 0)
--kmem.pg_refcount[V2P(v) >> PGSHIFT];
// No reference to the page -> free the page
if(kmem.pg_refcount[V2P(v) >> PGSHIFT] == 0)
{
// Fill with junk to catch dangling refs.
memset(v, 1, PGSIZE);
// When a page is freed
kmem.numFreePages++;
r->next = kmem.freelist;
kmem.freelist = r;
}
if(kmem.use_lock)
release(&kmem.lock);
}
char*
kalloc(void)
{
struct run *r;
if(kmem.use_lock)
acquire(&kmem.lock);
r = kmem.freelist;
if(r)
{
kmem.freelist = r->next;
//B10415024
// On a page allocation
kmem.numFreePages--;
// When allocate
kmem.pg_refcount[V2P((char*)r) >> PGSHIFT] = 1;
}
if(kmem.use_lock)
release(&kmem.lock);
return (char*)r;
}
//B10415011
uint
getNumFreePages(void)
{
acquire(&kmem.lock);
uint numFreePages = kmem.numFreePages;
release(&kmem.lock);
return numFreePages;
}
//B10415011
uint
getRefcount(uint pa)
{
if(pa < (uint)V2P(end) || pa >= PHYSTOP)
panic("getRefcount");
acquire(&kmem.lock);
uint count = kmem.pg_refcount[pa >> PGSHIFT];
release(&kmem.lock);
return count;
}
//B10415011
void
decrementRefcount(uint pa)
{
if(pa < (uint)V2P(end) || pa >= PHYSTOP)
panic("decrementRefcount");
acquire(&kmem.lock);
--kmem.pg_refcount[pa >> PGSHIFT];
release(&kmem.lock);
}
//B10415011
void
incrementRefcount(uint pa)
{
if(pa < (uint)V2P(end) || pa >= PHYSTOP)
panic("incrementRefcount");
acquire(&kmem.lock);
++kmem.pg_refcount[pa >> PGSHIFT];
release(&kmem.lock);
}
trap.c
void
trap(struct trapframe *tf)
{
...
switch(tf->trapno){
//B10415024
case T_PGFLT:
pagefault(tf->err);
break;
...
}
...
}
testcow.c
//B10415024
#include "types.h"
#include "stat.h"
#include "user.h"
int a = 1;
void
test1()
{
//1
printf(1, "Number of free pages : %d\n", getNumFreePages());
printf(1, "-Fork-\n");
int pid = fork();
//1
if(pid == 0)//code exec by child
{
//3
printf(1, "Child: 'a' = %d\n", a);
printf(1, "Number of free pages : %d\n", getNumFreePages());
printf(1, "-Change-\n");
a = 2;
printf(1, "Child: 'a' = %d\n", a);
printf(1, "Number of free pages : %d\n", getNumFreePages());
exit();
//3
}
//2
printf(1, "Parent: 'a' = %d\n", a);
wait();
//2
//4
printf(1, "-Reaping child-\n");
printf(1, "Patent: 'a' = %d\n", a);
printf(1, "Number of free pages : %d\n", getNumFreePages());
return;
//4
}
void
test2()
{
//1
printf(1, "Number of free pages : %d\n", getNumFreePages());
printf(1, "-First time Fork-\n");
int pid = fork();
//1
if(pid == 0)
//2.5
exit();
//2.5
else
{
//2
printf(1, "Number of free pages : %d\n", getNumFreePages());
printf(1, "-Second time Fork-\n");
pid = fork();
//2
if(pid == 0)
{
//5
printf(1, "Child: 'a' = %d\n", a);
printf(1, "Number of free pages : %d\n", getNumFreePages());
printf(1, "-Change-\n");
a = 5;
printf(1, "Child: 'a' = %d\n", a);
printf(1, "Number of free pages : %d\n", getNumFreePages());
exit();
//5
}
//3
printf(1, "Number of free pages : %d\n", getNumFreePages());
wait();
//
printf(1, "-Reaping child-\n");
//3
}
//4
printf(1, "Number of free pages : %d\n", getNumFreePages());
wait();
//4
//6
printf(1, "-Reaping child-\n");
printf(1, "Number of free pages : %d\n", getNumFreePages());
//6
return;
}
void
test3()
{
//1
a = 3;
printf(1, "Number of free pages : %d\n", getNumFreePages());
printf(1, "-First time Fork-\n");
int pid = fork();
//1
if(pid == 0)
{
//3
printf(1, "Child 1 : 'a' = %d\n", a);
printf(1, "Number of free pages : %d\n", getNumFreePages());
printf(1, "-Change-\n");
a = 4;
printf(1, "Child 1 : 'a' = %d\n", a);
printf(1, "Number of free pages : %d\n", getNumFreePages());
//3
}
else
{
//2
printf(1, "Number of free pages : %d\n", getNumFreePages());
wait();
//2
//4
printf(1, "-Reaping child-\n");
printf(1, "Number of free pages : %d\n", getNumFreePages());
printf(1, "-Second time Fork-\n");
pid = fork();
//4
if(pid == 0)
{
//6
printf(1, "Child 2 : 'a' = %d\n", a);
printf(1, "Number of free pages : %d\n", getNumFreePages());
printf(1, "-Change-\n");
a = 4;
printf(1, "Child 2 : 'a' = %d\n", a);
printf(1, "Number of free pages : %d\n", getNumFreePages());
//6
}
else
{
//5
printf(1, "Number of free pages : %d\n", getNumFreePages());
wait();
//5
//7
printf(1, "-Reaping child-\n");
printf(1, "Number of free pages : %d\n", getNumFreePages());
//7
}
}
return;
}
int
main(void)
{
printf(1, "Parent and Child share the global variable 'a'\n");
printf(1,"---run test1---\n");
test1();
printf(1,"---------------\n");
printf(1,"---run test2---\n");
test2();
printf(1,"---------------\n");
printf(1,"---run test3---\n");
test3();
printf(1,"---------------\n");
exit();
}
copy on write是對fork這個system call修改,主要是為了減少記憶體的用量。本來的fork是創一個child process,這個child process直接從parent的記憶體複製過來,花費相對較多的時間跟記憶體用量;而copy on write解決了這個問題,它是在child需要做更動時,才把記憶體內容複製過來,在未更動以前,child只從parent複製一部分的記憶體內容,另外沒有複製的部分是屬於與parent的共用區段。
君庭 B10415011:新增system call、處理page table、測試、Debug
珮均 B10415024:追蹤count、意外處理、測試、Debug