操作系统:在中断被阻塞的情况下切换到另一个进程不是错误的做法吗?

Operating System: Isn't it a wrong practice to switch to another process with the interrupt blocked?

我在 Linux 0.11 内核中看到磁盘上的这段代码:

static inline void lock_buffer(struct buffer_head * bh)
{
    cli();
    while (bh->b_lock)
        sleep_on(&bh->b_wait);
    bh->b_lock=1;
    sti();
}

IIUC,cli() 将阻止中断(不会像此处解释的那样阻止所有中断:https://c9x.me/x86/html/file_module_x86_id_31.html,但仍然会阻止一些中断,这意味着它会更改默认行为)。

并且sleep_on会调用schedule,这会将控制流传递给另一个进程。

然而,让我感到困惑的是,在这里我们将切换到另一个进程,其中一些中断被阻止,这似乎很容易出错,因为另一个进程应该期望默认行为。那么这是一段正确编写的代码(如果是,为什么?)还是只是一段错误的代码会导致意外行为?

又想了想。我认为这是预期的行为。这意味着在磁盘读取完成之前(unlock_buffer 被调用),所有后续执行都将处于不可中断模式(中断阻塞)。当缓冲区解锁并唤醒队列头时,

while (bh->b_lock)
  sleep_on(&bh->b_wait);
bh->b_lock=1;
sti();

将被执行,因为我们处于不可中断模式,它将执行到sti()而不切换到其他进程。因此,等待同一信号的其他进程将在计划时再次休眠(bh->b_lock 为 1),并且只有 1 个进程继续执行。

我假设磁盘驱动器的中断处理程序将被唤醒(&bh->b_wait),如果在等待这个过程中没有禁用中断,这可能会导致错过唤醒块。

记住条件变量(sleep_on,wakeup)是没有记忆的:sleep_on会挂起直到wakeup被调用;是否在 sleep_on.

之前调用 wakeup 并不重要

从测试bh->b_lock的时间点开始,调用者正在与中断处理程序赛跑;因此 cli(或者,更典型的 unix splbio())阻塞了中断处理程序,防止了竞争。

由于内核将中断状态(掩码,优先级,...)与进程状态一起保存,因此当sleep_on导致重新调度时,很可能会重新启用中断;或者至少最终会。磁盘中断最终会运行,唤醒这个进程。

当这个进程被重新调度时,它保存的中断状态(禁用)将被恢复,这样b_lock的测试和赋值也将防止磁盘中断处理程序的干扰。