如果您查看releasing a lock 的代码,您会发现它没有明确启用中断。相反,它使用函数popcli。
void release ( struct spinlock* lk )
{
...
popcli(); // enable interrupts
}
函数popcli 并不总是启用中断。它与pushcli 一起使用以跟踪嵌套级别。 “Pushcli/popcli 与 cli/sti 类似,只是它们是匹配的:需要两个 popcli 才能撤消两个 pushcli”。 1
void popcli ( void )
{
// If interrupts are enabled, panic...
if ( readeflags() & FL_IF )
{
panic( "popcli: interruptible" );
}
// Track depth of cli nesting
mycpu()->ncli -= 1;
// Popped more than were pushed...
if ( mycpu()->ncli < 0 )
{
panic( "popcli" );
}
// Reached outermost, so restore interrupt state
if ( mycpu()->ncli == 0 && mycpu()->intena )
{
sti(); // enable interrupts
}
}
popcli有时启用中断,pushcli总是禁用中断。
void pushcli ( void )
{
int eflags;
eflags = readeflags();
// Disable interrupts
cli();
// Save interrupt state at start of outermost
if ( mycpu()->ncli == 0 )
{
mycpu()->intena = eflags & FL_IF;
}
// Track depth of cli nesting
mycpu()->ncli += 1;
}
通过显式调用sti,调度程序会覆盖当前的 push/popcli 状态。我认为这提供了允许 IO 中断发生所需的简短窗口。 IE。调用sti 和调用cli 之间的时间段(通过acquire -> pushcli -> cli)。
void scheduler ( void )
{
...
for ( ;; )
{
// Enable interrupts on this processor.
sti();
// Acquire process table lock
acquire( &ptable.lock );
// Loop over process table looking for process to run.
for ( p = ptable.proc; p < &ptable.proc[ NPROC ]; p += 1 )
{
...
}
// Release process table lock
release( &ptable.lock );
}
}