xv6, line by line
test yourself

Test yourself · category 10 of 20

Sleep/wakeup and sleep-locks

How this tree’s sleep_prepare, sleep and wakeup avoid lost wakeups without passing a lock to sleep, why every sleep sits in a loop, and how sleep-locks are built on top of them.

1warm-upChoose one

wc finds the pipe empty, and piperead calls sleep_prepare(&pi->nread) on line 124. Right after that call returns, what has changed?

kernel/pipe.c
119 while (pi->nread == pi->nwrite && pi->writeopen) { //DOC: pipe-empty
120 if (killed(pr)) {
122 return -1;
123 }
124 sleep_prepare(&pi->nread); //DOC: piperead-sleep
128 }
2warm-upTrue or false, and why

True or false: in this kernel, the caller passes its condition lock to sleep, and sleep releases it before the process goes to sleep.

Why?

3warm-upChoose one

wakeup finds a process that is SLEEPING with p->chan equal to the channel. What state does it give that process?

kernel/proc.c
574// Wake up all processes sleeping on channel chan.
575void
578 struct proc *p;
580 for (p = proc; p < &proc[NPROC]; p++) {
582 if (p->chan == chan) {
583 // If the process is waiting for wakeups on this channel,
584 // signal that the wakeup happened by clearing p->chan.
585 p->chan = 0;
587 // If this waiting process has gotten so far as to actually
588 // go to sleep, also set it back to RUNNING.
589 if (p->state == SLEEPING) {
591 }
592 }
594 }
4warm-upChoose one

piperead sleeps on the channel &pi->nread. When pipewrite calls wakeup(&pi->nread), what does wakeup do with the integer stored at that address?

5warm-upChoose one

Why must piperead release pi->lock (line 125) before it calls sleep (line 126)?

kernel/pipe.c
119 while (pi->nread == pi->nwrite && pi->writeopen) { //DOC: pipe-empty
120 if (killed(pr)) {
122 return -1;
123 }
124 sleep_prepare(&pi->nread); //DOC: piperead-sleep
128 }
6warm-upChoose all that apply

Which of these are true of a sleep-lock but not of a spinlock, in this kernel?

7warm-upMatch the pairs

Match each place that sleeps with the channel it registers on.

8warm-upChoose one

Line 119 of piperead is a while, not an if. Why?

kernel/pipe.c
111int
112piperead(struct pipe *pi, uint64 addr, int n)
114 int i;
115 struct proc *pr = myproc();
116 char ch;
119 while (pi->nread == pi->nwrite && pi->writeopen) { //DOC: pipe-empty
120 if (killed(pr)) {
122 return -1;
123 }
124 sleep_prepare(&pi->nread); //DOC: piperead-sleep
128 }
9solidPut in order

wc reads from an empty pipe whose writer is still open and goes to sleep. Put the steps in order.

kernel/pipe.c
111int
112piperead(struct pipe *pi, uint64 addr, int n)
114 int i;
115 struct proc *pr = myproc();
116 char ch;
119 while (pi->nread == pi->nwrite && pi->writeopen) { //DOC: pipe-empty
120 if (killed(pr)) {
122 return -1;
123 }
124 sleep_prepare(&pi->nread); //DOC: piperead-sleep
128 }
  1. release(&pi->lock)
  2. sched() switches to this hart’s scheduler
  3. see nread == nwrite with writeopen set, and killed() returning 0
  4. acquire(&pi->lock)
  5. the scheduler releases wc’s p->lock
  6. sleep_prepare(&pi->nread): p->chan is set under p->lock
  7. sleep(): under p->lock, p->chan is still set, so p->state = SLEEPING
10solidClick the line

A process has called sleep_prepare but not yet sleep; it is still RUNNING. Another hart calls wakeup on its channel. Click the line of wakeup that makes the process’s coming sleep() return without sleeping.

kernel/proc.c
574// Wake up all processes sleeping on channel chan.
575void
578 struct proc *p;
580 for (p = proc; p < &proc[NPROC]; p++) {
582 if (p->chan == chan) {
583 // If the process is waiting for wakeups on this channel,
584 // signal that the wakeup happened by clearing p->chan.
585 p->chan = 0;
587 // If this waiting process has gotten so far as to actually
588 // go to sleep, also set it back to RUNNING.
589 if (p->state == SLEEPING) {
591 }
592 }
594 }

Your pick: none yet (click a line in the code)

11solidChoose one

wc is between lines 125 and 126 of pipe.c: registered on &pi->nread, pi->lock released, sleep() not yet called. On another hart, cat takes pi->lock, writes 512 bytes and calls wakeup(&pi->nread). What happens to wc?

kernel/pipe.c
119 while (pi->nread == pi->nwrite && pi->writeopen) { //DOC: pipe-empty
120 if (killed(pr)) {
122 return -1;
123 }
124 sleep_prepare(&pi->nread); //DOC: piperead-sleep
128 }
12solidChoose one

When pipewrite finds the pipe full, it calls wakeup(&pi->nread) on line 89 before it registers on &pi->nwrite and sleeps. What could happen without line 89?

kernel/pipe.c
76int
77pipewrite(struct pipe *pi, uint64 addr, int n)
79 int i = 0;
80 struct proc *pr = myproc();
83 while (i < n) {
84 if (pi->readopen == 0 || killed(pr)) {
86 return -1;
87 }
88 if (pi->nwrite == pi->nread + PIPESIZE) { //DOC: pipewrite-full
94 } else {
95 char ch;
96 if (copyin(pr->pagetable, pr->sz, &ch, addr + i, 1) == -1) {
97 if (i == 0)
98 i = -1;
99 break;
100 }
102 i++;
103 }
104 }
108 return i;
13solidChoose all that apply

A reader is SLEEPING in piperead, registered on &pi->nread. Which of these events can make it RUNNABLE?

14solidType a number

One call to wakeup runs on a machine where exactly one process is registered on the channel. How many times does that call execute acquire(&p->lock)?

kernel/proc.c
574// Wake up all processes sleeping on channel chan.
575void
578 struct proc *p;
580 for (p = proc; p < &proc[NPROC]; p++) {
582 if (p->chan == chan) {
583 // If the process is waiting for wakeups on this channel,
584 // signal that the wakeup happened by clearing p->chan.
585 p->chan = 0;
587 // If this waiting process has gotten so far as to actually
588 // go to sleep, also set it back to RUNNING.
589 if (p->state == SLEEPING) {
591 }
592 }
594 }
decimal, 0x hex or 0b binary
16solidFill in the machine state

A process is writing to the console. In uartwrite it holds tx_lock (a sleep-lock) and has just returned from sleep_prepare(&tx_chan) on line 86; it is about to read LSR on line 87. What is the state of its hart?

kernel/uart.c
79void
80uartwrite(char buf[], int n)
84 int i = 0;
85 while (i < n) {
89 i += 1;
90 } else {
92 }
93 }
17solidChoose one

holding checks a spinlock’s owner by hart (lk->cpu), but holdingsleep checks a sleep-lock’s owner by process ID (lk->pid). Why the difference?

kernel/sleeplock.c
46int
49 int r;
52 r = lk->locked && (lk->pid == myproc()->pid);
54 return r;
18solidPut in order

ls holds a buffer’s sleep-lock. grep calls acquiresleep on the same lock, and ls calls releasesleep only after grep is fully asleep. No third process wants the lock. Put the events in order.

kernel/sleeplock.c
21void
25 while (lk->locked) {
30 }
31 lk->locked = 1;
32 lk->pid = myproc()->pid;
36void
40 lk->locked = 0;
41 lk->pid = 0;
  1. ls: wakeup(lk) makes grep RUNNABLE
  2. grep: re-acquires lk->lk, sees locked == 0, sets locked = 1 and pid
  3. grep: sleep_prepare(lk) sets grep’s p->chan = lk
  4. grep: acquire(&lk->lk), sees locked == 1
  5. grep: sleep() marks grep SLEEPING and switches away
  6. ls: under lk->lk, locked = 0 and pid = 0
  7. grep: release(&lk->lk)
20deepFill in the machine state

Hart 0 is idle in its scheduler. At the top of the loop, line 441 turns interrupts on and the pending timer interrupt is taken at once. clockintr holds tickslock and is inside wakeup(&ticks), holding one process’s p->lock. What is the state of hart 0?

kernel/trap.c
166void
169 if (cpuid() == 0) {
174 }
176 // ask for the next timer interrupt. this also clears
177 // the interrupt request. 1000000 is about a tenth
178 // of a second.
179 w_stimecmp(r_time() + 1000000);
21deepTrue or false, and why

True or false: once kkill has set wc’s killed flag, wc cannot go to sleep in piperead's wait loop until it has noticed the kill.

kernel/pipe.c
119 while (pi->nread == pi->nwrite && pi->writeopen) { //DOC: pipe-empty
120 if (killed(pr)) {
122 return -1;
123 }
124 sleep_prepare(&pi->nread); //DOC: piperead-sleep
128 }

Why?

22deepChoose one

uartwrite sleeps on &tx_chan without holding any spinlock: its condition is the LSR_TX_IDLE bit of a device register, and the waker is uartintr. Why can’t the “transmitter is idle” wakeup be lost?

kernel/uart.c
79void
80uartwrite(char buf[], int n)
84 int i = 0;
85 while (i < n) {
89 i += 1;
90 } else {
92 }
93 }
23deepChoose one

iput calls acquiresleep(&ip->lock) on line 362 while holding the spinlock itable.lock. Why doesn’t this break the rule “never sleep while holding a spinlock”?

kernel/fs.c
349void
350iput(struct inode *ip)
354 // Last reference of an unlinked inode? Capture dev/inum before ref--,
355 // since once ref hits 0, ip may be recycled by a concurrent iget()
356 // for a different inum.
357 int last = (ip->ref == 1 && ip->valid && ip->nlink == 0);
360 if (last) {
361 // ip->ref == 1 means no other process can have ip locked.
365 itrunc(ip); // free the data blocks (type stays nonzero on disk)
366 ip->valid = 0;
371 }
373 ip->ref--;
376 if (last)
377 ifree(dev, inum); // now clear type on disk: inum becomes allocatable
24deepTrue or false, and why

True or false: in kexit, swapping lines 353 and 355, so that the process takes its own p->lock before calling wakeup(p->parent), would be harmless.

kernel/proc.c
349 // Give any children to init.
352 // Parent might be sleeping in wait().
362 // Jump into the scheduler, never to return.
364 panic("zombie exit");

Why?

25deepChoose all that apply

A process P has p->chan != 0. Which of these could be true at that moment?