diff --git a/Notes b/Notes
index 63eb0c7e98ec85e0d4d8112f0fc9c0597769a7c7..d8f4869a44e9dbc0bf343fcc6a27579a515e2e99 100644
--- a/Notes
+++ b/Notes
@@ -80,16 +80,22 @@ trap() ought to lgdt on return, since currently only done in swtch()
 
 protect hardware interrupt vectors from user INT instructions?
 
-i'm getting a curious interrupt when jumping into user space. maybe
-it's IRQ 0, but it comes at a weird and changing vector (e.g. 119) if
-you don't initialize the PIC. why doesn't jos see this? if i
-initialize the PIC with IRQ_OFFSET 32, the interrupt arrives at vector
-32.
-
 test out-of-fd cases for creating pipe.
-test pipe circular buffer
-test pipe writer or reader closes while other active or waiting
-test exit vs fd reference counts
-test write of more than PIPESIZE
-test reader goes first vs writer goes first
-test streaming of a lot of data
+test pipe reader closes then write
+test two readers, two writers.
+test children being inherited by grandparent &c
+
+kill
+  sleep()ing for something
+  running at user level
+  running in kernel
+  ooh, the relevant CPU may never get a clock interrupt
+  should each cpu have its own clock?
+  where to check?
+    loops around sleep()
+    return from any trap
+  rules about being killed deep inside a system call
+  test above cases
+
+cli/sti in acquire/release should nest!
+  in case you acquire two locks
diff --git a/defs.h b/defs.h
index 9e10bee4e40194272959acf28b7e438ae95afd44..6a13ad27a5477172b6e921541d7f0c5ad7bda5b1 100644
--- a/defs.h
+++ b/defs.h
@@ -17,6 +17,8 @@ void swtch(void);
 void sleep(void *);
 void wakeup(void *);
 void scheduler(void);
+void proc_exit(void);
+void yield(void);
 
 // swtch.S
 struct jmpbuf;
diff --git a/kalloc.c b/kalloc.c
index 1944508eaa4ad677c6c9bfa84f12846366a6365e..b14a69a329a6fd8925f3fb5a53d3d28f5237b38f 100644
--- a/kalloc.c
+++ b/kalloc.c
@@ -158,6 +158,4 @@ ktest()
   if(p1 == 0)
     panic("ktest2");
   kfree(p1, PAGE * 20);
-
-  cprintf("ktest ok\n");
 }
diff --git a/main.c b/main.c
index b711640f96d07e2d844742bd6ec4941ac4893b92..ce29af3a9e3d38d29d4c2004832982f13fab1e9f 100644
--- a/main.c
+++ b/main.c
@@ -66,11 +66,12 @@ main()
   ide_init(); 
 
   // become interruptable
-  write_eflags(read_eflags() | FL_IF);
+  sti();
 
   p = newproc();
-  // load_icode(p, _binary_usertests_start, (unsigned) _binary_usertests_size);
-  load_icode(p, _binary_userfs_start, (unsigned) _binary_userfs_size);
+  
+  load_icode(p, _binary_usertests_start, (unsigned) _binary_usertests_size);
+  //load_icode(p, _binary_userfs_start, (unsigned) _binary_userfs_size);
   cprintf("loaded userfs\n");
   scheduler();
 
diff --git a/proc.c b/proc.c
index bdff3772506eb8b41a274102b547862a476b3fd4..d7fc6388c49ef6c8415458952280dd037fe34b00 100644
--- a/proc.c
+++ b/proc.c
@@ -184,3 +184,45 @@ wakeup(void *chan)
     if(p->state == WAITING && p->chan == chan)
       p->state = RUNNABLE;
 }
+
+// give up the CPU but stay marked as RUNNABLE
+void
+yield()
+{
+  if(curproc[cpu()] == 0 || curproc[cpu()]->state != RUNNING)
+    panic("yield");
+  curproc[cpu()]->state = RUNNABLE;
+  swtch();
+}
+
+void
+proc_exit()
+{
+  struct proc *p;
+  struct proc *cp = curproc[cpu()];
+  int fd;
+
+  cprintf("exit %x\n", cp);
+
+  for(fd = 0; fd < NOFILE; fd++){
+    if(cp->fds[fd]){
+      fd_close(cp->fds[fd]);
+      cp->fds[fd] = 0;
+    }
+  }
+
+  cp->state = ZOMBIE;
+
+  // wake up parent
+  for(p = proc; p < &proc[NPROC]; p++)
+    if(p->pid == cp->ppid)
+      wakeup(p);
+
+  // abandon children
+  for(p = proc; p < &proc[NPROC]; p++)
+    if(p->ppid == cp->pid)
+      p->pid = 1;
+
+  // switch into scheduler
+  swtch();
+}
diff --git a/proc.h b/proc.h
index b5cf015c5cc83b91996cb46587d4bda280da77b4..86ba0ebccbaa7994099f38c8c1c1acd1860261c2 100644
--- a/proc.h
+++ b/proc.h
@@ -41,6 +41,7 @@ struct proc{
   int pid;
   int ppid;
   void *chan; // sleep
+  int killed;
   struct fd *fds[NOFILE];
 
   struct Taskstate ts;  // only to give cpu address of kernel stack
diff --git a/spinlock.c b/spinlock.c
index 8d402587d7b9341141bcf8333c1889a97cd1a24a..d73faff03174e348502d62d3ba897b9f9e8917f8 100644
--- a/spinlock.c
+++ b/spinlock.c
@@ -20,7 +20,7 @@ acquire_spinlock(uint32_t* lock)
 
   // on a real machine there would be a memory barrier here
   if(DEBUG) cprintf("cpu%d: acquiring at %x\n", cpu_id, getcallerpc(&lock));
-  write_eflags(read_eflags() & ~FL_IF);
+  cli();
   if (*lock == cpu_id)
     panic("recursive lock");
   
@@ -37,7 +37,7 @@ release_spinlock(uint32_t* lock)
     panic("release_spinlock: releasing a lock that i don't own\n");
   *lock = LOCK_FREE;
   // on a real machine there would be a memory barrier here
-  write_eflags(read_eflags() | FL_IF);
+  sti();
 }
 
 void
diff --git a/syscall.c b/syscall.c
index 03fe608aad3969dd535ce14bd7de912c130b16a3..4ecb31cb395f5be8a11e1f25ec1fcb0150f277ea 100644
--- a/syscall.c
+++ b/syscall.c
@@ -155,32 +155,7 @@ sys_fork()
 int
 sys_exit()
 {
-  struct proc *p;
-  struct proc *cp = curproc[cpu()];
-  int fd;
-
-  for(fd = 0; fd < NOFILE; fd++){
-    if(cp->fds[fd]){
-      fd_close(cp->fds[fd]);
-      cp->fds[fd] = 0;
-    }
-  }
-
-  cp->state = ZOMBIE;
-
-  // wake up parent
-  for(p = proc; p < &proc[NPROC]; p++)
-    if(p->pid == cp->ppid)
-      wakeup(p);
-
-  // abandon children
-  for(p = proc; p < &proc[NPROC]; p++)
-    if(p->ppid == cp->pid)
-      p->pid = 1;
-
-  // switch into scheduler
-  swtch();
-
+  proc_exit();
   return 0;
 }
 
@@ -250,6 +225,24 @@ sys_block(void)
   return 0;
 }
 
+int
+sys_kill()
+{
+  int pid;
+  struct proc *p;
+
+  fetcharg(0, &pid);
+  for(p = proc; p < &proc[NPROC]; p++){
+    if(p->pid == pid && p->state != UNUSED){
+      p->killed = 1;
+      if(p->state == WAITING)
+        p->state = RUNNABLE;
+      return 0;
+    }
+  }
+  return -1;
+}
+
 void
 syscall()
 {
@@ -286,6 +279,9 @@ syscall()
   case SYS_block:
     ret = sys_block();
     break;
+  case SYS_kill:
+    ret = sys_kill();
+    break;
   default:
     cprintf("unknown sys call %d\n", num);
     // XXX fault
diff --git a/syscall.h b/syscall.h
index 0378c53f75105b113ab894ad1ebac9303f54eeb8..6a76893e3ccf14fd8f3c5ab3afccdc2317f8cbd2 100644
--- a/syscall.h
+++ b/syscall.h
@@ -7,3 +7,4 @@
 #define SYS_read 7
 #define SYS_close 8
 #define SYS_block 9
+#define SYS_kill 10
diff --git a/trap.c b/trap.c
index d177d0445a3aaae394c25d4f744fad7a58e1eacb..d7739f73fb0d008905e1064df2a04986c2b67d85 100644
--- a/trap.c
+++ b/trap.c
@@ -45,6 +45,8 @@ trap(struct Trapframe *tf)
     struct proc *cp = curproc[cpu()];
     if(cp == 0)
       panic("syscall with no proc");
+    if(cp->killed)
+      proc_exit();
     cp->tf = tf;
     syscall();
     if(cp != curproc[cpu()])
@@ -55,11 +57,20 @@ trap(struct Trapframe *tf)
       panic("trap ret wrong tf");
     if(read_esp() < (unsigned)cp->kstack || read_esp() >= (unsigned)cp->kstack + KSTACKSIZE)
       panic("trap ret esp wrong");
+    if(cp->killed)
+      proc_exit();
     return;
   }
 
   if(v == (IRQ_OFFSET + IRQ_TIMER)){
+    struct proc *cp = curproc[cpu()];
     lapic_timerintr();
+    if(cp){
+      sti();
+      if(cp->killed)
+        proc_exit();
+      yield();
+    }
     return;
   }
   if(v == (IRQ_OFFSET + IRQ_IDE)){
diff --git a/usertests.c b/usertests.c
index 37540dbbdb2acce751e53e442c5d4c3736a2c9b5..2f688ca98d82e1894f29f8700d01cc973699a40e 100644
--- a/usertests.c
+++ b/usertests.c
@@ -1,7 +1,7 @@
-// simple fork and pipe read/write
-
 char buf[2048];
 
+// simple fork and pipe read/write
+
 void
 pipe1()
 {
@@ -47,9 +47,54 @@ pipe1()
   puts("pipe1 ok\n");
 }
 
+// meant to be run w/ at most two CPUs
+void
+preempt()
+{
+  int pid1, pid2, pid3;
+  int pfds[2];
+
+  pid1 = fork();
+  if(pid1 == 0)
+    while(1)
+      ;
+    
+  pid2 = fork();
+  if(pid2 == 0)
+    while(1)
+      ;
+
+  pipe(pfds);
+  pid3 = fork();
+  if(pid3 == 0){
+    close(pfds[0]);
+    if(write(pfds[1], "x", 1) != 1)
+      puts("preempt write error");
+    close(pfds[1]);
+    while(1)
+      ;
+  }
+
+  close(pfds[1]);
+  if(read(pfds[0], buf, sizeof(buf)) != 1){
+    puts("preempt read error");
+    return;
+  }
+  close(pfds[0]);
+  kill(pid1);
+  kill(pid2);
+  kill(pid3);
+  wait();
+  wait();
+  wait();
+  puts("preempt ok\n");
+}
+
 main()
 {
+  puts("usertests starting\n");
   pipe1();
+  //preempt();
 
   while(1)
     ;
diff --git a/usys.S b/usys.S
index c399d62fbf4226522dd547ac612540127c94268e..53958c1629dd969c3fae5f98c58c62919de1b801 100644
--- a/usys.S
+++ b/usys.S
@@ -10,9 +10,11 @@
 
 STUB(fork)
 STUB(exit)
+STUB(wait)
 STUB(cons_putc)
 STUB(pipe)
 STUB(read)
 STUB(write)
 STUB(close)
 STUB(block)
+STUB(kill)