6.8 Sleep locks
Sometimes xv6 needs to hold a lock for a long time. For example, the file system (Chapter 8) keeps a file locked while reading and writing its content on the disk, and these disk operations can take tens of milliseconds. Holding a spinlock that long would lead to waste if another process wanted to acquire it, since the acquiring process would waste CPU for a long time while spinning. Another drawback of spinlocks is that a process cannot yield the CPU while retaining a spinlock; we’d like to do this so that other processes can use the CPU while the process with the lock waits for the disk. Yielding while holding a spinlock is illegal because it might lead to deadlock if a second thread then tried to acquire the spinlock; since acquire doesn’t yield the CPU, the second thread’s spinning might prevent the first thread from running and releasing the lock. Yielding while holding a lock would also violate the requirement that interrupts must be off while a spinlock is held. Thus we’d like a type of lock that yields the CPU while waiting to acquire, and allows yields (and interrupts) while the lock is held.
Xv6 provides such locks in the form of
sleep-locks.
acquiresleep
(kernel/sleeplock.c:22)
yields the CPU while waiting,
using techniques that will be explained in
Chapter 7.
At a high level, a sleep-lock has a
locked
field that is protected by a spinlock, and
acquiresleep
’s
call to
sleep
atomically yields the CPU and releases the spinlock.
The result is that other threads can execute while
acquiresleep
waits.
Because sleep-locks leave interrupts enabled, they cannot be
used in interrupt handlers.
Because
acquiresleep
may yield the CPU,
sleep-locks cannot be used inside spinlock critical
sections (though spinlocks can be used inside sleep-lock
critical sections).
Spin-locks are best suited to short critical sections, since waiting for them wastes CPU time; sleep-locks work well for lengthy operations.