diff options
Diffstat (limited to 'kernel/proc.c')
-rw-r--r-- | kernel/proc.c | 123 |
1 files changed, 66 insertions, 57 deletions
diff --git a/kernel/proc.c b/kernel/proc.c index a947f7f..6ba3fec 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -6,20 +6,16 @@ #include "proc.h" #include "defs.h" -struct proc proc[NPROC]; - struct cpu cpus[NCPU]; +struct proc proc[NPROC]; + struct proc *initproc; -struct spinlock pid_lock; int nextpid = 1; +struct spinlock pid_lock; extern void forkret(void); - -// for returning out of the kernel -extern void sysexit(void); - static void wakeup1(struct proc *chan); extern char trampout[]; // trampoline.S @@ -68,8 +64,10 @@ allocpid() { int pid; acquire(&pid_lock); - pid = nextpid++; + pid = nextpid; + nextpid = nextpid + 1; release(&pid_lock); + return pid; } @@ -77,7 +75,7 @@ allocpid() { // Look in the process table for an UNUSED proc. // If found, initialize state required to run in the kernel, // and return with p->lock held. -// Otherwise return 0. +// If there are no free procs, return 0. static struct proc* allocproc(void) { @@ -98,6 +96,7 @@ found: // Allocate a page for the kernel stack. if((p->kstack = kalloc()) == 0){ + release(&p->lock); return 0; } @@ -105,6 +104,7 @@ found: if((p->tf = (struct trapframe *)kalloc()) == 0){ kfree(p->kstack); p->kstack = 0; + release(&p->lock); return 0; } @@ -145,16 +145,13 @@ freeproc(struct proc *p) } // Create a page table for a given process, -// with no users pages, but with trampoline pages. -// Called both when creating a process, and -// by exec() when building tentative new memory image, -// which might fail. +// with no user pages, but with trampoline pages. pagetable_t proc_pagetable(struct proc *p) { pagetable_t pagetable; - // An empty user page table. + // An empty page table. pagetable = uvmcreate(); // map the trampoline code (for system call return) @@ -172,9 +169,7 @@ proc_pagetable(struct proc *p) } // Free a process's page table, and free the -// physical memory the page table refers to. -// Called both when a process exits and from -// exec() if it fails. +// physical memory it refers to. void proc_freepagetable(pagetable_t pagetable, uint64 sz) { @@ -187,9 +182,12 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz) // a user program that calls exec("/init") // od -t xC initcode uchar initcode[] = { - 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x02, 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x05, 0x02, - 0x9d, 0x48, 0x73, 0x00, 0x00, 0x00, 0x89, 0x48, 0x73, 0x00, 0x00, 0x00, 0xef, 0xf0, 0xbf, 0xff, - 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x02, + 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x05, 0x02, + 0x9d, 0x48, 0x73, 0x00, 0x00, 0x00, 0x89, 0x48, + 0x73, 0x00, 0x00, 0x00, 0xef, 0xf0, 0xbf, 0xff, + 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x00, 0x01, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -203,12 +201,14 @@ userinit(void) p = allocproc(); initproc = p; + // allocate one user page and copy init's instructions + // and data into it. uvminit(p->pagetable, initcode, sizeof(initcode)); p->sz = PGSIZE; // prepare for the very first "return" from kernel to user. - p->tf->epc = 0; - p->tf->sp = PGSIZE; + p->tf->epc = 0; // user program counter + p->tf->sp = PGSIZE; // user stack pointer safestrcpy(p->name, "initcode", sizeof(p->name)); p->cwd = namei("/"); @@ -218,7 +218,7 @@ userinit(void) release(&p->lock); } -// Grow current process's memory by n bytes. +// Grow or shrink user memory by n bytes. // Return 0 on success, -1 on failure. int growproc(int n) @@ -240,8 +240,8 @@ growproc(int n) return 0; } -// Create a new process, copying p as the parent. -// Sets up child kernel stack to return as if from system call. +// Create a new process, copying the parent. +// Sets up child kernel stack to return as if from fork() system call. int fork(void) { @@ -287,8 +287,8 @@ fork(void) return pid; } -// Pass p's abandoned children to init. p and p's parent -// are locked. +// Pass p's abandoned children to init. +// Caller must hold p->lock and parent->lock. void reparent(struct proc *p, struct proc *parent) { struct proc *pp; @@ -312,7 +312,6 @@ reparent(struct proc *p, struct proc *parent) { } } - // Exit the current process. Does not return. // An exited process remains in the zombie state // until its parent calls wait(). @@ -343,6 +342,7 @@ exit(void) acquire(&p->lock); + // Give our children to init. reparent(p, p->parent); p->state = ZOMBIE; @@ -366,24 +366,32 @@ wait(void) int havekids, pid; struct proc *p = myproc(); + // hold p->lock for the whole time to avoid lost + // wakeups from a child's exit(). acquire(&p->lock); + for(;;){ // Scan through table looking for exited children. havekids = 0; for(np = proc; np < &proc[NPROC]; np++){ - if(np->parent != p) - continue; - acquire(&np->lock); - havekids = 1; - if(np->state == ZOMBIE){ - // Found one. - pid = np->pid; - freeproc(np); + // this code uses np->parent without holding np->lock. + // acquiring the lock first would cause a deadlock, + // since np might be an ancestor, and we already hold p->lock. + if(np->parent == p){ + // np->parent can't change between the check and the acquire() + // because only the parent changes it, and we're the parent. + acquire(&np->lock); + havekids = 1; + if(np->state == ZOMBIE){ + // Found one. + pid = np->pid; + freeproc(np); + release(&np->lock); + release(&p->lock); + return pid; + } release(&np->lock); - release(&p->lock); - return pid; } - release(&np->lock); } // No point waiting if we don't have any children. @@ -392,7 +400,7 @@ wait(void) return -1; } - // Wait for children to exit. (See wakeup1 call in reparent.) + // Wait for a child to exit. sleep(p, &p->lock); //DOC: wait-sleep } } @@ -401,10 +409,10 @@ wait(void) // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. // Scheduler never returns. It loops, doing: -// - choose a process to run -// - swtch to start running that process +// - choose a process to run. +// - swtch to start running that process. // - eventually that process transfers control -// via swtch back to the scheduler. +// via swtch back to the scheduler. void scheduler(void) { @@ -413,7 +421,7 @@ scheduler(void) c->proc = 0; for(;;){ - // Enable interrupts on this processor. + // Give devices a brief chance to interrupt. intr_on(); for(p = proc; p < &proc[NPROC]; p++) { @@ -435,7 +443,7 @@ scheduler(void) } } -// Enter scheduler. Must hold only p->lock +// Switch to scheduler. Must hold only p->lock // and have changed proc->state. Saves and restores // intena because intena is a property of this // kernel thread, not this CPU. It should @@ -512,12 +520,13 @@ sleep(void *chan, struct spinlock *lk) // change p->state and then call sched. // Once we hold p->lock, we can be // guaranteed that we won't miss any wakeup - // (wakeup runs with p->lock locked), + // (wakeup locks p->lock), // so it's okay to release lk. if(lk != &p->lock){ //DOC: sleeplock0 acquire(&p->lock); //DOC: sleeplock1 release(lk); } + // Go to sleep. p->chan = chan; p->state = SLEEPING; @@ -535,8 +544,8 @@ sleep(void *chan, struct spinlock *lk) } //PAGEBREAK! -// Wake up p, used by exit() -// Caller should lock p. +// Wake up p if it is sleeping in wait(); used by exit(). +// Caller must hold p->lock. static void wakeup1(struct proc *p) { @@ -545,8 +554,8 @@ wakeup1(struct proc *p) } } -// Wake up all processes sleeping on chan. Never -// called when holding a p->lock +// Wake up all processes sleeping on chan. +// Must be called without any p->lock. void wakeup(void *chan) { @@ -562,25 +571,25 @@ wakeup(void *chan) } // Kill the process with the given pid. -// Process won't exit until it returns -// to user space (see trap in trap.c). +// The victim won't exit until it tries to return +// to user space (see usertrap() in trap.c). int kill(int pid) { struct proc *p; for(p = proc; p < &proc[NPROC]; p++){ + acquire(&p->lock); if(p->pid == pid){ - acquire(&p->lock); - if(p->pid != pid) - panic("kill"); p->killed = 1; - // Wake process from sleep if necessary. - if(p->state == SLEEPING) + if(p->state == SLEEPING){ + // Wake process from sleep(). p->state = RUNNABLE; + } release(&p->lock); return 0; } + release(&p->lock); } return -1; } |