Rearrange proc.h and proc.c to get our action-packed spreads back (mostly). They also make sense in this order, so it's not just for page layout.

This commit is contained in:
Austin Clements 2010-09-02 04:15:17 -04:00
parent dd3ecd42cd
commit d8828817d7
3 changed files with 126 additions and 120 deletions

172
proc.c
View file

@ -193,6 +193,92 @@ fork(void)
return pid; 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 //PAGEBREAK: 42
// Per-CPU process scheduler. // Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up. // Each CPU calls scheduler() after setting itself up.
@ -357,89 +443,3 @@ kill(int pid)
return -1; 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
}
}

59
proc.h
View file

@ -8,6 +8,36 @@
#define SEG_TSS 6 // this process's task state #define SEG_TSS 6 // this process's task state
#define NSEGS 7 #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. // Saved registers for kernel context switches.
// Don't need to save all the segment registers (%cs, etc), // Don't need to save all the segment registers (%cs, etc),
// because they are constant across kernel contexts. // because they are constant across kernel contexts.
@ -50,32 +80,3 @@ struct proc {
// original data and bss // original data and bss
// fixed-size stack // fixed-size stack
// expandable heap // 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.

View file

@ -20,22 +20,27 @@ sheet1: left
even: bootasm.S # mild preference even: bootasm.S # mild preference
even: bootother.S # mild preference even: bootother.S # mild preference
even: bootmain.S # mild preference even: bootmain.c # mild preference
even: main.c even: main.c
# mp.c don't care at all # mp.c don't care at all
# even: initcode.S # even: initcode.S
# odd: init.c # odd: init.c
# spinlock.h either # spinlock.h either
left: spinlock.c # mild preference left: spinlock.h # mild preference
even: proc.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, # goal is to have two action-packed 2-page spreads,
# one with # one with
# allocproc userinit growproc fork # userinit growproc fork exit wait
# and another with # and another with
# scheduler sched yield forkret sleep wakeup1 wakeup # 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 # setjmp.S either
# vm.c either # vm.c either