7.9 Process Locking
The lock associated with each process (p->lock) is the
most complex lock in xv6.
A simple way to think about p->lock is
that it must be held while reading or writing any of the following
struct proc fields:
p->state,
p->chan,
p->killed,
p->xstate,
and
p->pid.
These fields can be used by other processes, or by scheduler
threads on other CPUs, so it’s natural that they
must be protected by a lock.
However, most uses of p->lock are protecting higher-level
aspects of xv6’s process data structures and algorithms. Here’s
the full set of things that p->lock does:
-
•
Along with
p->state, it prevents races in allocatingproc[]slots for new processes. -
•
It conceals a process from view while it is being created or destroyed.
-
•
It prevents a parent’s
waitfrom collecting a process that has set its state toZOMBIEbut has not yet yielded the CPU. -
•
It prevents another CPU’s scheduler from deciding to run a yielding process after it sets its state to
RUNNABLEbut before it finishesswtch. -
•
It ensures that only one CPU’s scheduler decides to run a
RUNNABLEprocesses. -
•
It prevents a timer interrupt from causing a process to yield while it is in
swtch. -
•
Along with the condition lock, it helps prevent
wakeupfrom overlooking a process that is callingsleepbut has not finished yielding the CPU. -
•
It prevents the victim process of
killfrom exiting and perhaps being re-allocated betweenkill’s check ofp->pidand settingp->killed. -
•
It makes
kill’s check and write ofp->stateatomic.
The p->parent field is protected by the global lock
wait_lock rather than by p->lock.
Only a process’s parent modifies p->parent, though
the field is read both by the process itself and by other
processes searching for their children. The purpose of
wait_lock is to act as the condition lock when
wait sleeps waiting for any child to exit. An
exiting child holds either wait_lock or p->lock
until after it has set its state to ZOMBIE, woken
up its parent, and yielded the CPU. wait_lock also
serializes concurrent exits by a parent and child,
so that the init process (which inherits the child)
is guaranteed to be woken up from its wait.
wait_lock is a global lock rather than a per-process
lock in each parent, because, until a process acquires it,
it cannot know who its parent is.