3

配套书说

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

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

4

2 回答 2

1

可能会schedule()在禁用中断的情况下调用它,在这种情况下,释放 ptable 自旋锁不会重新启用它们。

于 2015-05-07T15:10:10.003 回答
1

如果您查看释放锁的代码,您会发现它没有显式启用中断。相反,它使用函数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 );
    }
}
于 2020-06-16T02:58:37.417 回答