As learning XV6's spinlock,I thought a problem and don't find answer in google.
In spinlock, pushcli()
and popcli()
used to insure interrupt handler run currently:
void pushcli(void)
{
int eflags;
eflags = readeflags();
cli();
if (mycpu()->ncli == 0)
mycpu()->intena = eflags & FL_IF;
mycpu()->ncli += 1;
}
void popcli(void)
{
if (readeflags() & FL_IF)
panic("popcli - interruptible");
if (--mycpu()->ncli < 0)
panic("popcli");
if (mycpu()->ncli == 0 && mycpu()->intena)
sti();
}
However, the spinlock is used to dure with threads and func in it like xchg()
should be atomic,but codes in pushcli()
and popcli
like mycpu()->ncli += 1;
are not atomic , which might cause incorrect result of ncli and intena.So why we can use nonatomic operations in pushcli()
?If we shouldn't, what can we do to correct it to safe?
Short answer:
mycpu()->ncli
is local to each CPUlk->locked
is shared between all CPUsNo pushcli
and popcli
don't need be atomic because they can't be preempted.
We have two different context here:
The access to spinlock->locked
(lk->locked
) member
// The xchg is atomic.
while(xchg(&lk->locked, 1) != 0)
;
And the enabling/disable of interruption: cli()
/sti()
In the first case, the variable is stored in memory, any cpu could want to access the lock at any time, the atomic instruction is need here: only one cpu can have access to it.
In the second case, we are in the kernel code of a cpu: the code executed is local to given processor and once cli
is called the cpu can't interrupt (so can't be preempted) until sti
is called.
Since the cpu deals with a variable that is private to it mycpu()->ncli;
, no atomicity is needed here.
To be exact, mycpu()
read a shared array cpus[]
, and by design return the cpu
structure referring to the CPU that executed the function. Note that mycpy()
require interrupt to be disabled to execute (if not, you got a kernal panic).