diff --git a/proc.c b/proc.c index 375b1bb..8197377 100644 --- a/proc.c +++ b/proc.c @@ -193,6 +193,92 @@ fork(void) return pid; } +// 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. +void +exit(void) +{ + struct proc *p; + int fd; + + if(proc == initproc) + panic("init exiting"); + + // Close all open files. + for(fd = 0; fd < NOFILE; fd++){ + if(proc->ofile[fd]){ + fileclose(proc->ofile[fd]); + proc->ofile[fd] = 0; + } + } + + iput(proc->cwd); + proc->cwd = 0; + + acquire(&ptable.lock); + + // Parent might be sleeping in wait(). + wakeup1(proc->parent); + + // Pass abandoned children to init. + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->parent == proc){ + p->parent = initproc; + if(p->state == ZOMBIE) + wakeup1(initproc); + } + } + + // Jump into the scheduler, never to return. + proc->state = ZOMBIE; + sched(); + panic("zombie exit"); +} + +// Wait for a child process to exit and return its pid. +// Return -1 if this process has no children. +int +wait(void) +{ + struct proc *p; + int havekids, pid; + + acquire(&ptable.lock); + for(;;){ + // Scan through table looking for zombie children. + havekids = 0; + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->parent != proc) + continue; + havekids = 1; + if(p->state == ZOMBIE){ + // Found one. + pid = p->pid; + kfree(p->kstack); + p->kstack = 0; + freevm(p->pgdir); + p->state = UNUSED; + p->pid = 0; + p->parent = 0; + p->name[0] = 0; + p->killed = 0; + release(&ptable.lock); + return pid; + } + } + + // No point waiting if we don't have any children. + if(!havekids || proc->killed){ + release(&ptable.lock); + return -1; + } + + // Wait for children to exit. (See wakeup1 call in proc_exit.) + sleep(proc, &ptable.lock); //DOC: wait-sleep + } +} + //PAGEBREAK: 42 // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. @@ -357,89 +443,3 @@ kill(int pid) return -1; } -// 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. -void -exit(void) -{ - struct proc *p; - int fd; - - if(proc == initproc) - panic("init exiting"); - - // Close all open files. - for(fd = 0; fd < NOFILE; fd++){ - if(proc->ofile[fd]){ - fileclose(proc->ofile[fd]); - proc->ofile[fd] = 0; - } - } - - iput(proc->cwd); - proc->cwd = 0; - - acquire(&ptable.lock); - - // Parent might be sleeping in wait(). - wakeup1(proc->parent); - - // Pass abandoned children to init. - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->parent == proc){ - p->parent = initproc; - if(p->state == ZOMBIE) - wakeup1(initproc); - } - } - - // Jump into the scheduler, never to return. - proc->state = ZOMBIE; - sched(); - panic("zombie exit"); -} - -// Wait for a child process to exit and return its pid. -// Return -1 if this process has no children. -int -wait(void) -{ - struct proc *p; - int havekids, pid; - - acquire(&ptable.lock); - for(;;){ - // Scan through table looking for zombie children. - havekids = 0; - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->parent != proc) - continue; - havekids = 1; - if(p->state == ZOMBIE){ - // Found one. - pid = p->pid; - kfree(p->kstack); - p->kstack = 0; - freevm(p->pgdir); - p->state = UNUSED; - p->pid = 0; - p->parent = 0; - p->name[0] = 0; - p->killed = 0; - release(&ptable.lock); - return pid; - } - } - - // No point waiting if we don't have any children. - if(!havekids || proc->killed){ - release(&ptable.lock); - return -1; - } - - // Wait for children to exit. (See wakeup1 call in proc_exit.) - sleep(proc, &ptable.lock); //DOC: wait-sleep - } -} - diff --git a/proc.h b/proc.h index 8799668..4a80a28 100644 --- a/proc.h +++ b/proc.h @@ -8,6 +8,36 @@ #define SEG_TSS 6 // this process's task state #define NSEGS 7 +// Per-CPU state +struct cpu { + uchar id; // Local APIC ID; index into cpus[] below + struct context *scheduler; // Switch here to enter scheduler + struct taskstate ts; // Used by x86 to find stack for interrupt + struct segdesc gdt[NSEGS]; // x86 global descriptor table + volatile uint booted; // Has the CPU started? + int ncli; // Depth of pushcli nesting. + int intena; // Were interrupts enabled before pushcli? + + // Cpu-local storage variables; see below + struct cpu *cpu; + struct proc *proc; +}; + +extern struct cpu cpus[NCPU]; +extern int ncpu; + +// Per-CPU variables, holding pointers to the +// current cpu and to the current process. +// The asm suffix tells gcc to use "%gs:0" to refer to cpu +// and "%gs:4" to refer to proc. ksegment sets up the +// %gs segment register so that %gs refers to the memory +// holding those two variables in the local cpu's struct cpu. +// This is similar to how thread-local variables are implemented +// in thread libraries such as Linux pthreads. +extern struct cpu *cpu asm("%gs:0"); // This cpu. +extern struct proc *proc asm("%gs:4"); // Current proc on this cpu. + +//PAGEBREAK: 17 // Saved registers for kernel context switches. // Don't need to save all the segment registers (%cs, etc), // because they are constant across kernel contexts. @@ -50,32 +80,3 @@ struct proc { // original data and bss // fixed-size stack // expandable heap - -// Per-CPU state -struct cpu { - uchar id; // Local APIC ID; index into cpus[] below - struct context *scheduler; // Switch here to enter scheduler - struct taskstate ts; // Used by x86 to find stack for interrupt - struct segdesc gdt[NSEGS]; // x86 global descriptor table - volatile uint booted; // Has the CPU started? - int ncli; // Depth of pushcli nesting. - int intena; // Were interrupts enabled before pushcli? - - // Cpu-local storage variables; see below - struct cpu *cpu; - struct proc *proc; -}; - -extern struct cpu cpus[NCPU]; -extern int ncpu; - -// Per-CPU variables, holding pointers to the -// current cpu and to the current process. -// The asm suffix tells gcc to use "%gs:0" to refer to cpu -// and "%gs:4" to refer to proc. ksegment sets up the -// %gs segment register so that %gs refers to the memory -// holding those two variables in the local cpu's struct cpu. -// This is similar to how thread-local variables are implemented -// in thread libraries such as Linux pthreads. -extern struct cpu *cpu asm("%gs:0"); // This cpu. -extern struct proc *proc asm("%gs:4"); // Current proc on this cpu. diff --git a/runoff.spec b/runoff.spec index fe960ce..9d87528 100644 --- a/runoff.spec +++ b/runoff.spec @@ -20,22 +20,27 @@ sheet1: left even: bootasm.S # mild preference even: bootother.S # mild preference -even: bootmain.S # mild preference +even: bootmain.c # mild preference even: main.c # mp.c don't care at all # even: initcode.S # odd: init.c # spinlock.h either -left: spinlock.c # mild preference -even: proc.h # mild preference +left: spinlock.h # mild preference +even: spinlock.h # mild preference + +# This gets struct proc and allocproc on the same spread +right: proc.h +odd: proc.h # goal is to have two action-packed 2-page spreads, # one with -# allocproc userinit growproc fork +# userinit growproc fork exit wait # and another with # scheduler sched yield forkret sleep wakeup1 wakeup -right: proc.c # VERY important +left: proc.c # VERY important +odd: proc.c # VERY important # setjmp.S either # vm.c either