fork/wait/exit work
This commit is contained in:
parent
0f90388c89
commit
2ec1959fd1
30 changed files with 1098 additions and 1863 deletions
285
proc.c
285
proc.c
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue