配套书说
在空闲CPU上定期启用中断的原因是 可能没有RUNNABLE进程因为进程(例如shell) 等待I / O;如果调度程序中断所有禁用中断 时间,I / O永远不会到达。
但是我认为我们只需要在out-for循环之前调用sti()一次,因为每次我们释放ptable.lock时,都会再次启用中断。
答案 0 :(得分:1)
在禁用中断的情况下调用schedule()
是可能的,在这种情况下释放ptable自旋锁将不会重新启用它们。
答案 1 :(得分:0)
如果查看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中断发生的简短窗口。即呼叫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 );
}
}