为什么xv6调度程序在每个循环的开头调用sti()?

时间:2015-05-07 12:24:22

标签: operating-system kernel xv6

配套书说

  

在空闲CPU上定期启用中断的原因是   可能没有RUNNABLE进程因为进程(例如shell)   等待I / O;如果调度程序中断所有禁用中断   时间,I / O永远不会到达。

但是我认为我们只需要在out-for循环之前调用sti()一次,因为每次我们释放ptable.lock时,都会再次启用中断。

2 个答案:

答案 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 );
    }
}