have fork() fail, not panic, if not enough phys mem

This commit is contained in:
Robert Morris 2019-07-01 17:46:06 -04:00
parent 18e76a6c47
commit abfe9999f4
5 changed files with 94 additions and 16 deletions

View file

@ -128,6 +128,7 @@ UPROGS=\
$U/_usertests\
$U/_wc\
$U/_zombie\
$U/_cow\
fs.img: mkfs/mkfs README $(UPROGS)
mkfs/mkfs fs.img README $(UPROGS)

View file

@ -185,7 +185,7 @@ pagetable_t uvmcreate(void);
void uvminit(pagetable_t, uchar *, uint);
uint64 uvmalloc(pagetable_t, uint64, uint64);
uint64 uvmdealloc(pagetable_t, uint64, uint64);
void uvmcopy(pagetable_t, pagetable_t, uint64);
int uvmcopy(pagetable_t, pagetable_t, uint64);
void uvmfree(pagetable_t, uint64);
void mappages(pagetable_t, uint64, uint64, uint64, int);
void unmappages(pagetable_t, uint64, uint64, int);

View file

@ -109,6 +109,28 @@ found:
return p;
}
// free a proc structure and the data hanging from it,
// including user pages.
// the proc lock must be held.
static void
freeproc(struct proc *p)
{
if(p->kstack)
kfree(p->kstack);
p->kstack = 0;
if(p->tf)
kfree((void*)p->tf);
p->tf = 0;
if(p->pagetable)
proc_freepagetable(p->pagetable, p->sz);
p->pagetable = 0;
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->killed = 0;
p->state = UNUSED;
}
// Create a page table for a given process,
// with no users pages, but with trampoline pages.
// Called both when creating a process, and
@ -145,6 +167,7 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz)
{
unmappages(pagetable, TRAMPOLINE, PGSIZE, 0);
unmappages(pagetable, TRAMPOLINE-PGSIZE, PGSIZE, 0);
if(sz > 0)
uvmfree(pagetable, sz);
}
@ -223,7 +246,10 @@ fork(void)
}
// Copy user memory from parent to child.
uvmcopy(p->pagetable, np->pagetable, p->sz);
if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
freeproc(np);
return -1;
}
np->sz = p->sz;
np->parent = p;
@ -319,17 +345,7 @@ wait(void)
if(np->state == ZOMBIE){
// Found one.
pid = np->pid;
kfree(np->kstack);
np->kstack = 0;
kfree((void*)np->tf);
np->tf = 0;
proc_freepagetable(np->pagetable, np->sz);
np->pagetable = 0;
np->pid = 0;
np->parent = 0;
np->name[0] = 0;
np->killed = 0;
np->state = UNUSED;
freeproc(np);
release(&ptable.lock);
return pid;
}

View file

@ -273,7 +273,9 @@ uvmfree(pagetable_t pagetable, uint64 sz)
// its memory into a child's page table.
// Copies both the page table and the
// physical memory.
void
// returns 0 on success, -1 on failure.
// frees any allocated pages on failure.
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
pte_t *pte;
@ -289,10 +291,15 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
pa = PTE2PA(*pte);
flags = PTE_FLAGS(*pte);
if((mem = kalloc()) == 0)
panic("uvmcopy: kalloc failed");
goto err;
memmove(mem, (char*)pa, PGSIZE);
mappages(new, i, PGSIZE, (uint64)mem, flags);
}
return 0;
err:
unmappages(new, 0, i, 1);
return -1;
}
// Copy from kernel to user.

54
user/cow.c Normal file
View file

@ -0,0 +1,54 @@
//
// tests for copy-on-write fork() assignment.
//
#include "kernel/types.h"
#include "kernel/memlayout.h"
#include "user/user.h"
// allocate more than half of physical memory,
// then fork. this will fail in the default
// kernel, which does not support copy-on-write.
void
simpletest()
{
uint64 phys_size = PHYSTOP - KERNBASE;
int sz = (phys_size / 3) * 2;
printf(1, "simple: ");
char *p = sbrk(sz);
if(p == (char*)0xffffffffffffffffL){
printf(1, "sbrk(%d) failed\n", sz);
exit();
}
for(char *q = p; q < p + sz; q += 4096){
*(int*)q = getpid();
}
int pid = fork();
if(pid < 0){
printf(1, "fork() failed\n");
exit();
}
if(pid == 0)
exit();
wait();
if(sbrk(-sz) == (char*)0xffffffffffffffffL){
printf(1, "sbrk(-%d) failed\n", sz);
exit();
}
printf(1, "simple ok\n");
}
int
main(int argc, char *argv[])
{
simpletest();
exit();
}