summaryrefslogtreecommitdiff
path: root/kernel/proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/proc.c')
-rw-r--r--kernel/proc.c123
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;
}