xv6, line by line
tour 14
Tours14 pause(n) and the tick counter

Tour 14 · Time and scheduling · about 26 minutes · 18 steps

pause(n) and the tick counter

xv6 has exactly one notion of time: ticks, a counter that goes up by one about ten times a second. Only hart 0 increments it. Every process that wants to wait for time to pass, through the pause system call, sleeps on the address of that counter and is woken at every tick to check whether it has waited long enough.

This tour follows zombie (pid 3), which calls pause(5) on hart 2 to give its child a head start. You will see sys_pause read the clock under tickslock, register on the channel &ticks and go to sleep, while hart 0, in clockintr, does ticks++ and wakeup(&ticks), possibly at the very instant zombie is about to sleep. You will see why that race is harmless, why zombie wakes up five times, why it may finish its pause on a different hart, and why a kill can cut the wait short.

It is a small system call, and a complete lesson in the sleep and wakeup pattern, Tour 16: sleep and wakeup, and the lost-wakeup problem, applied to a device every hart has: the clock.

Best after: 5. Life of a system call, 11. From a timer tick to a context switch, 16. sleep and wakeup, and the lost-wakeup problem

Who is running where

The machine has three harts. As the first command after boot, you typed zombie at the shell (pid 2, now asleep in wait). The shell’s child, pid 3, ran zombie, which has just forked pid 4. When the tour starts:

Hart What it is doing
0 Idle in its scheduler: but it is the hart that counts ticks
1 Running pid 4, the child, which is calling exit(0)
2 Running zombie (pid 3) in user mode: the process this tour follows
Three harts are running. This tour follows one path through the code, but the machine has three CPUs executing at the same time. Watch the locks held display at the top of each step, and read the Meanwhile, on other harts boxes: they show what the other CPUs could be doing at that very moment.
The route
  1. 1zombie decides to wait half a second user/zombie.c
  2. 2The usual doorway into the kernel user/usys.S
  3. 3Fetch n, and refuse negative time kernel/sysproc.c
  4. 4Where the clock lives kernel/trap.c
  5. 5Read the clock under the lock kernel/sysproc.c
  6. 6The loop test, and a check for kill kernel/sysproc.c
  7. 7Register on &ticks while the clock cannot move kernel/proc.c
  8. 8Let go of the clock kernel/sysproc.c
  9. 9zombie goes to sleep kernel/proc.c
  10. 10Hart 0's timer fires, and hart 0 counts kernel/trap.c
  11. 11wakeup visits every process kernel/proc.c
  12. 12Re-arm, and pick up the process just woken kernel/trap.c
  13. 13zombie wakes, on hart 0 kernel/proc.c
  14. 14Not yet. Again kernel/sysproc.c
  15. 15Only hart 0 keeps time kernel/trap.c
  16. 16If kill arrives during the pause kernel/sysproc.c
  17. 17The fifth tick kernel/sysproc.c
  18. 18What half a second cost user/zombie.c

Keys: ← → step · Home start