fork/wait/exit work

This commit is contained in:
Robert Morris 2019-05-31 09:45:59 -04:00
parent 0f90388c89
commit 2ec1959fd1
30 changed files with 1098 additions and 1863 deletions

285
proc.c
View file

@ -1,18 +1,20 @@
#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "x86.h"
#include "riscv.h"
#include "proc.h"
#include "spinlock.h"
#include "defs.h"
struct {
struct spinlock lock;
struct proc proc[NPROC];
} ptable;
static struct proc *initproc;
// XXX riscv move somewhere else
struct cpu cpus[NCPU];
struct proc *initproc;
int nextpid = 1;
extern void forkret(void);
@ -22,57 +24,36 @@ extern void sysexit(void);
static void wakeup1(void *chan);
extern char trampstart[]; // trampoline.S
void
pinit(void)
procinit(void)
{
initlock(&ptable.lock, "ptable");
}
// Must be called with interrupts disabled
// Must be called with interrupts disabled.
// XXX riscv
int
cpuid() {
return mycpu()-cpus;
return 0;
}
// Must be called with interrupts disabled to avoid the caller being
// rescheduled between reading lapicid and running through the loop.
struct cpu*
getmycpu(void)
{
int apicid, i;
if(readeflags()&FL_IF)
panic("getmycpu called with interrupts enabled\n");
apicid = lapicid();
// APIC IDs are not guaranteed to be contiguous.
for (i = 0; i < ncpu; ++i) {
if (cpus[i].apicid == apicid)
return &cpus[i];
}
panic("unknown apicid\n");
}
// Return this core's cpu struct using %gs. %gs points this core's struct
// cpu. Offet 24 in struct cpu is cpu.
// Return this core's cpu struct.
// XXX riscv
struct cpu*
mycpu(void) {
struct cpu *c;
asm volatile("mov %%gs:24, %0" : "=r" (c));
c = &cpus[0];
return c;
}
// Disable interrupts so that we are not rescheduled
// while reading proc from the cpu structure
// XXX riscv
struct proc*
myproc(void) {
struct cpu *c;
struct proc *p;
pushcli();
c = mycpu();
p = c->proc;
popcli();
return p;
return cpus[0].proc;
}
//PAGEBREAK: 32
@ -84,7 +65,6 @@ static struct proc*
allocproc(void)
{
struct proc *p;
char *sp;
acquire(&ptable.lock);
@ -101,56 +81,73 @@ found:
release(&ptable.lock);
// Allocate kernel stack.
// Allocate a page for the kernel stack.
if((p->kstack = kalloc()) == 0){
p->state = UNUSED;
return 0;
}
sp = p->kstack + KSTACKSIZE;
// Leave room for syscall frame.
sp -= sizeof *p->sf;
// Allocate a trapframe page.
if((p->tf = (struct trapframe *)kalloc()) == 0){
p->state = UNUSED;
return 0;
}
if ((uint64) sp % 16)
panic("misaligned sp");
// An empty user page table.
p->pagetable = uvmcreate();
p->sf = (struct sysframe*)sp;
// map the trampoline code (for system call return)
// at the highest user virtual address.
// only the supervisor uses it, on the way
// to/from user space, so not PTE_U.
mappages(p->pagetable, TRAMPOLINE, PGSIZE,
(uint64)trampstart, PTE_R | PTE_X);
// map the trapframe, for trampoline.S.
mappages(p->pagetable, (TRAMPOLINE - PGSIZE), PGSIZE,
(uint64)(p->tf), PTE_R | PTE_W);
// Set up new context to start executing at forkret,
// which returns to sysexit.
sp -= sizeof(uint64);
*(uint64*)sp = (uint64)sysexit;
sp -= sizeof *p->context;
p->context = (struct context*)sp;
memset(p->context, 0, sizeof *p->context);
p->context->rip = (uint64)forkret;
// which returns to user space.
memset(&p->context, 0, sizeof p->context);
p->context.ra = (uint64)forkret;
p->context.sp = (uint64)p->kstack + PGSIZE;
return p;
}
// XXX hack because I don't know how to incorporate initcode
// into the kernel binary. just the exec system call, no arguments.
// manually copied from initcode.asm.
unsigned char initcode[] = {
0x85, 0x48, // li a7, 1 -- SYS_fork
0x73, 0x00, 0x00, 0x00, // ecall
0x8d, 0x48, // li a7, 3 -- SYS_wait
0x73, 0x00, 0x00, 0x00, // ecall
0x89, 0x48, // li a7, 2 -- SYS_exit
0x73, 0x00, 0x00, 0x00, // ecall
};
//PAGEBREAK: 32
// Set up first user process.
void
userinit(void)
{
struct proc *p;
extern char _binary_initcode_start[], _binary_initcode_size[];
p = allocproc();
initproc = p;
if((p->pgdir = setupkvm()) == 0)
panic("userinit: out of memory?");
inituvm(p->pgdir, _binary_initcode_start, (uint64)_binary_initcode_size);
uvminit(p->pagetable, initcode, sizeof(initcode));
p->sz = PGSIZE;
memset(p->sf, 0, sizeof(*p->sf));
p->sf->r11 = FL_IF;
p->sf->rsp = PGSIZE;
p->sf->rcx = 0; // beginning of initcode.S
// prepare for the very first kernel->user.
p->tf->epc = 0;
p->tf->sp = PGSIZE;
safestrcpy(p->name, "initcode", sizeof(p->name));
p->cwd = namei("/");
// XXX riscv
//p->cwd = namei("/");
// this assignment to p->state lets other cores
// run this process. the acquire forces the above
@ -163,62 +160,65 @@ userinit(void)
release(&ptable.lock);
}
#if 0
// Grow current process's memory by n bytes.
// Return 0 on success, -1 on failure.
int
growproc(int n)
{
uint sz;
struct proc *curproc = myproc();
struct proc *p = myproc();
sz = curproc->sz;
sz = p->sz;
if(n > 0){
if((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0)
if((sz = allocuvm(p->pagetable, sz, sz + n)) == 0)
return -1;
} else if(n < 0){
if((sz = deallocuvm(curproc->pgdir, sz, sz + n)) == 0)
if((sz = uvmdealloc(p->pagetable, sz, sz + n)) == 0)
return -1;
}
curproc->sz = sz;
switchuvm(curproc);
p->sz = sz;
switchuvm(p);
return 0;
}
#endif
// Create a new process copying p as the parent.
// Sets up stack to return as if from system call.
// Caller must set state of returned proc to RUNNABLE.
// Create a new process, copying p as the parent.
// Sets up child kernel stack to return as if from system call.
int
fork(void)
{
int i, pid;
struct proc *np;
struct proc *curproc = myproc();
struct proc *p = myproc();
// Allocate process.
if((np = allocproc()) == 0){
return -1;
}
// Copy process state from proc.
if((np->pgdir = copyuvm(curproc->pgdir, curproc->sz)) == 0){
kfree(np->kstack);
np->kstack = 0;
np->state = UNUSED;
return -1;
}
np->sz = curproc->sz;
np->parent = curproc;
*np->sf = *curproc->sf;
// Copy user memory from parent to child.
uvmcopy(p->pagetable, np->pagetable, p->sz);
np->sz = p->sz;
// Clear %eax so that fork returns 0 in the child.
np->sf->rax = 0;
np->parent = p;
// copy saved user registers.
*(np->tf) = *(p->tf);
// Cause fork to return 0 in the child.
np->tf->a0 = 0;
#if 0 // XXX riscv
// increment reference counts on open file descriptors.
for(i = 0; i < NOFILE; i++)
if(curproc->ofile[i])
np->ofile[i] = filedup(curproc->ofile[i]);
np->cwd = idup(curproc->cwd);
if(p->ofile[i])
np->ofile[i] = filedup(p->ofile[i]);
np->cwd = idup(p->cwd);
#endif
safestrcpy(np->name, curproc->name, sizeof(curproc->name));
safestrcpy(np->name, p->name, sizeof(p->name));
pid = np->pid;
@ -233,46 +233,48 @@ fork(void)
// Exit the current process. Does not return.
// An exited process remains in the zombie state
// until its parent calls wait() to find out it exited.
// until its parent calls wait().
void
exit(void)
{
struct proc *curproc = myproc();
struct proc *p;
struct proc *p = myproc();
struct proc *pp;
int fd;
if(curproc == initproc)
if(p == initproc)
panic("init exiting");
#if 0 // XXX riscv
// Close all open files.
for(fd = 0; fd < NOFILE; fd++){
if(curproc->ofile[fd]){
fileclose(curproc->ofile[fd]);
curproc->ofile[fd] = 0;
if(p->ofile[fd]){
fileclose(p->ofile[fd]);
p->ofile[fd] = 0;
}
}
begin_op();
iput(curproc->cwd);
iput(p->cwd);
end_op();
curproc->cwd = 0;
#endif
p->cwd = 0;
acquire(&ptable.lock);
// Parent might be sleeping in wait().
wakeup1(curproc->parent);
wakeup1(p->parent);
// Pass abandoned children to init.
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->parent == curproc){
p->parent = initproc;
if(p->state == ZOMBIE)
for(pp = ptable.proc; pp < &ptable.proc[NPROC]; pp++){
if(pp->parent == p){
pp->parent = initproc;
if(pp->state == ZOMBIE)
wakeup1(initproc);
}
}
// Jump into the scheduler, never to return.
curproc->state = ZOMBIE;
p->state = ZOMBIE;
sched();
panic("zombie exit");
}
@ -282,42 +284,47 @@ exit(void)
int
wait(void)
{
struct proc *p;
struct proc *np;
int havekids, pid;
struct proc *curproc = myproc();
struct proc *p = myproc();
acquire(&ptable.lock);
for(;;){
// Scan through table looking for exited children.
havekids = 0;
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->parent != curproc)
for(np = ptable.proc; np < &ptable.proc[NPROC]; np++){
if(np->parent != p)
continue;
havekids = 1;
if(p->state == ZOMBIE){
if(np->state == ZOMBIE){
// Found one.
pid = p->pid;
kfree(p->kstack);
p->kstack = 0;
freevm(p->pgdir, p->sz);
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->killed = 0;
p->state = UNUSED;
pid = np->pid;
kfree(np->kstack);
np->kstack = 0;
kfree((void*)np->tf);
np->tf = 0;
unmappages(np->pagetable, TRAMPOLINE, PGSIZE, 0);
unmappages(np->pagetable, TRAMPOLINE-PGSIZE, PGSIZE, 0);
uvmfree(np->pagetable, np->sz);
np->pagetable = 0;
np->pid = 0;
np->parent = 0;
np->name[0] = 0;
np->killed = 0;
np->state = UNUSED;
release(&ptable.lock);
return pid;
}
}
// No point waiting if we don't have any children.
if(!havekids || curproc->killed){
if(!havekids || p->killed){
release(&ptable.lock);
return -1;
}
// Wait for children to exit. (See wakeup1 call in proc_exit.)
sleep(curproc, &ptable.lock); //DOC: wait-sleep
sleep(p, &ptable.lock); //DOC: wait-sleep
}
}
@ -338,7 +345,8 @@ scheduler(void)
c->proc = 0;
for(;;){
// Enable interrupts on this processor.
sti();
// XXX riscv
//sti();
// Loop over process table looking for process to run.
acquire(&ptable.lock);
@ -350,11 +358,11 @@ scheduler(void)
// to release ptable.lock and then reacquire it
// before jumping back to us.
c->proc = p;
switchuvm(p);
p->state = RUNNING;
swtch(&(c->scheduler), p->context);
switchkvm();
printf("switch...\n");
swtch(&c->scheduler, &p->context);
printf("switch returned\n");
// Process is done running for now.
// It should have changed its p->state before coming back.
@ -380,14 +388,10 @@ sched(void)
if(!holding(&ptable.lock))
panic("sched ptable.lock");
if(mycpu()->ncli != 1)
panic("sched locks");
if(p->state == RUNNING)
panic("sched running");
if(readeflags()&FL_IF)
panic("sched interruptible");
intena = mycpu()->intena;
swtch(&p->context, mycpu()->scheduler);
swtch(&p->context, &mycpu()->scheduler);
mycpu()->intena = intena;
}
@ -402,24 +406,29 @@ yield(void)
}
// A fork child's very first scheduling by scheduler()
// will swtch here. "Return" to user space.
// will swtch to forkret.
void
forkret(void)
{
struct proc *p = myproc();
static int first = 1;
// Still holding ptable.lock from scheduler.
release(&ptable.lock);
printf("entering forkret\n");
if (first) {
// Some initialization functions must be run in the context
// of a regular process (e.g., they call sleep), and thus cannot
// be run from main().
first = 0;
iinit(ROOTDEV);
initlog(ROOTDEV);
// XXX riscv
//iinit(ROOTDEV);
//initlog(ROOTDEV);
}
// Return to "caller", actually trapret (see allocproc).
usertrapret();
}
// Atomically release lock and sleep on chan.
@ -483,6 +492,8 @@ wakeup(void *chan)
release(&ptable.lock);
}
#if 0
// Kill the process with the given pid.
// Process won't exit until it returns
// to user space (see trap in trap.c).
@ -533,12 +544,14 @@ procdump(void)
state = states[p->state];
else
state = "???";
cprintf("%d %s %s", p->pid, state, p->name);
printf("%d %s %s", p->pid, state, p->name);
if(p->state == SLEEPING){
getcallerpcs((uint64*)p->context->rbp+2, pc);
for(i=0; i<10 && pc[i] != 0; i++)
cprintf(" %p", pc[i]);
printf(" %p", pc[i]);
}
cprintf("\n");
printf("\n");
}
}
#endif