7.4 Code: mycpu and myproc
Xv6 often needs a pointer to the current process’s proc
structure. On a uniprocessor one could have a global variable pointing
to the current proc
. This doesn’t work on a multi-core
machine, since each CPU executes a different process. The way to
solve this problem is to exploit the fact that each CPU has its own
set of registers.
While a given CPU is executing in the kernel, xv6 ensures that the CPU’s tp
register always holds the CPU’s hartid.
RISC-V numbers its CPUs, giving each
a unique hartid.
mycpu
(kernel/proc.c:74)
uses tp
to index an array
of cpu
structures and return the one
for the current CPU.
A
struct cpu
(kernel/proc.h:22)
holds a pointer to the proc
structure of
the process currently running
on that CPU (if any),
saved registers for the CPU’s scheduler thread,
and the count of nested spinlocks needed to manage
interrupt disabling.
Ensuring that a CPU’s tp
holds the CPU’s
hartid is a little involved, since user code is free
to modify tp
. start
sets the tp
register early in the CPU’s boot sequence, while still in machine mode
(kernel/start.c:45).
usertrapret
saves tp
in the trampoline
page, in case user code modifies it.
Finally, uservec
restores that saved tp
when entering the kernel from user space
(kernel/trampoline.S:78).
The compiler guarantees never to modify tp
in kernel code.
It would be more convenient if xv6 could ask the RISC-V
hardware for the current hartid whenever needed,
but RISC-V allows that only in
machine mode, not in supervisor mode.
The return values of
cpuid
and
mycpu
are fragile: if the timer were to interrupt and cause
the thread to yield and later resume execution on a different CPU,
a previously returned value would no longer be correct.
To avoid this problem, xv6 requires that callers
disable interrupts, and only enable
them after they finish using the returned
struct cpu
.
The function
myproc
(kernel/proc.c:83)
returns the
struct proc
pointer
for the process that is running on the current CPU.
myproc
disables interrupts, invokes
mycpu
,
fetches the current process pointer
(c->proc
)
out of the
struct cpu
,
and then enables interrupts.
The return value of
myproc
is safe to use even if interrupts are enabled:
if a timer interrupt moves the calling process to a
different CPU, its
struct proc
pointer will stay the same.