Reputation: 1857
The companion book says
The reason to enable interrupts periodically on an idling CPU is that there might be no RUNNABLE process because processes (e.g., the shell) are waiting for I/O; if the scheduler left interrupts disabled all the time, the I/O would never arrive.
But I think we just need to call sti() once before the outter for-loop, since everytime we release ptable.lock, the interrupts are enabled again.
Upvotes: 3
Views: 2604
Reputation: 5281
If you look at the code for releasing a lock, you will see that it does not explicitly enable interrupts. Instead it uses the function popcli
.
void release ( struct spinlock* lk )
{
...
popcli(); // enable interrupts
}
The function popcli
does not always enable interrupts. It is used in tandem with pushcli
to track nesting level. "Pushcli/popcli are like cli/sti except that they are matched: it takes two popcli to undo two 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
}
}
Whereas popcli
sometimes enables interrupts, pushcli
always disables interrupts.
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;
}
By explicitly calling sti
, the scheduler overrides whatever the current push/popcli state is. I think this provides the brief window desired to allow an IO interrupt to occur. I.e. the period of time between the call to sti
and the call to cli
(via 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 );
}
}
Upvotes: 1
Reputation: 20520
It's possible that schedule()
is called with interrupts disabled, in which case releasing the ptable spinlock will not reenable them.
Upvotes: 1