Simplify copyin/copyinstr(hard)
将定义在kernel/vm.c中的copyin的主题内容替换为对copyin_new的调用
(在kernel/vmcopyin.c中定义)
对copyinstr和copyinstr_new执行相同的操作。
diff --git a/kernel/vm.c b/kernel/vm.c
index 0984442..ad8ca06 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -427,23 +427,7 @@ copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{
uint64 n, va0, pa0;
while(len > 0){
va0 = PGROUNDDOWN(srcva);
pa0 = walkaddr(pagetable, va0);
if(pa0 == 0)
return -1;
n = PGSIZE - (srcva - va0);
if(n > len)
n = len;
memmove(dst, (void *)(pa0 + (srcva - va0)), n);
len -= n;
dst += n;
srcva = va0 + PGSIZE;
}
return 0;
return copyin_new(pagetable, dst, srcva, len);
}
// Copy a null-terminated string from user to kernel.
@@ -453,40 +437,7 @@ copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
int
copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
{
uint64 n, va0, pa0;
int got_null = 0;
while(got_null == 0 && max > 0){
va0 = PGROUNDDOWN(srcva);
pa0 = walkaddr(pagetable, va0);
if(pa0 == 0)
return -1;
n = PGSIZE - (srcva - va0);
if(n > max)
n = max;
char *p = (char *) (pa0 + (srcva - va0));
while(n > 0){
if(*p == '\0'){
*dst = '\0';
got_null = 1;
break;
} else {
*dst = *p;
}
--n;
--max;
p++;
dst++;
}
srcva = va0 + PGSIZE;
}
if(got_null){
return 0;
} else {
return -1;
}
return copyinstr_new(pagetable, dst, srcva, max);
}
void
在内核更改进程的用户映射的每一处,都以相同的方式更改进程的内核页表。
包括fork(), exec(), 和sbrk().
既然如此使用相同的方式,先写一个复制函数
复制函数
diff --git a/kernel/vm.c b/kernel/vm.c
index 0984442..ad8ca06 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -524,4 +475,27 @@ void
vmprint(pagetable_t pagetable){
printf("page table %p\n", pagetable);
vmprint_sub(pagetable, 0);
}
int u2kvmcopy(pagetable_t upagetable, pagetable_t kpagetable, uint64 begin, uint64 end) {
pte_t *pte;
uint64 pa, i;
uint flags;
uint64 begin_page = PGROUNDUP(begin); // 向上取整
for(i = begin_page; i < end; i += PGSIZE){
if((pte = walk(upagetable, i, 0)) == 0)
panic("uvmcopy2kvm: pte should exist");
if((*pte & PTE_V) == 0)
panic("uvmcopy2kvm: page not present");
pa = PTE2PA(*pte);
flags = PTE_FLAGS(*pte) & (~PTE_U); // clear PTE_U flag
if(mappages(kpagetable, i, PGSIZE, pa, flags) != 0){
goto err;
}
}
return 0;
err:
uvmunmap(kpagetable, begin_page, (i- begin_page) / PGSIZE, 0);
return -1;
}
\ No newline at end of file
添加到fork()
diff --git a/kernel/proc.c b/kernel/proc.c
index ad394df..9e246cd 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -315,6 +326,12 @@ fork(void)
}
np->sz = p->sz;
if(u2kvmcopy(np->pagetable, np->prockpt, 0, np->sz) < 0) {
freeproc(np);
release(&np->lock);
return -1;
}
np->parent = p;
// copy saved user registers.
添加到exec()
diff --git a/kernel/exec.c b/kernel/exec.c
index fc832f7..f97826b 100644
--- a/kernel/exec.c
+++ b/kernel/exec.c
@@ -116,6 +116,11 @@ exec(char *path, char **argv)
p->trapframe->sp = sp; // initial stack pointer
proc_freepagetable(oldpagetable, oldsz);
uvmunmap(p->prockpt, 0, PGROUNDUP(oldsz)/PGSIZE, 0);
if(u2kvmcopy(p->pagetable, p->prockpt, 0, p->sz) < 0){
goto bad;
}
if(p->pid==1) {
vmprint(p->pagetable);
}
修改 growproc()
sbrk() 函数即系统调用 sys_brk() 函数, 最终会调用 kernel/proc.c 中的 growproc() 函数, 用来增长或减少虚拟内存空间
diff --git a/kernel/proc.c b/kernel/proc.c
index ad394df..9e246cd 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -283,11 +285,20 @@ growproc(int n)
sz = p->sz;
if(n > 0){
if ((sz + n) > PLIC){
return -1;
}
if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {
return -1;
}
if(u2kvmcopy(p->pagetable, p->prockpt, p->sz, sz) < 0){
return -1;
}
} else if(n < 0){
sz = uvmdealloc(p->pagetable, sz, sz + n);
if (PGROUNDUP(sz) < PGROUNDUP(p->sz)) {
uvmunmap(p->prockpt, PGROUNDUP(sz), (PGROUNDUP(p->sz) - PGROUNDUP(sz)) / PGSIZE, 0);
}
}
p->sz = sz;
return 0;
修改userinit()
userinit() 的作用是初始化 xv6 启动的第一个用户进程, 进程的加载是独立的, 需要将其用户页表拷贝到内核页表.
diff --git a/kernel/proc.c b/kernel/proc.c
index ad394df..9e246cd 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -261,6 +261,8 @@ userinit(void)
uvminit(p->pagetable, initcode, sizeof(initcode));
p->sz = PGSIZE;
u2kvmcopy(p->pagetable, p->prockpt, 0, p->sz);
// prepare for the very first "return" from kernel to user.
p->trapframe->epc = 0; // user program counter
p->trapframe->sp = PGSIZE; // user stack pointer
将需要的函数定义添加到 kernel/defs.h 中
diff --git a/kernel/defs.h b/kernel/defs.h
index 2e302d5..33f1ff5 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -182,6 +182,11 @@ int copyout(pagetable_t, uint64, char *, uint64);
int copyin(pagetable_t, char *, uint64, uint64);
int copyinstr(pagetable_t, char *, uint64, uint64);
void vmprint(pagetable_t);
int u2kvmcopy(pagetable_t, pagetable_t, uint64, uint64);
// vmcopyin.c
int copyin_new(pagetable_t, char *, uint64, uint64);
int copyinstr_new(pagetable_t, char *, uint64, uint64);
// plic.c
void plicinit(void);
diff --git a/kernel/vm.c b/kernel/vm.c
index 0984442..ad8ca06 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -62,7 +62,7 @@ prockptinit()
uvmmap(prockpt, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
// CLINT
uvmmap(prockpt, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
// uvmmap(prockpt, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
// PLIC
uvmmap(prockpt, PLIC, PLIC, 0x400000, PTE_R | PTE_W);