Lunarain_079's Inn

Back

在sched()函数中,有一个看似矛盾的操作:先检查中断是否已关闭,然后又保存CPU的中断启用状态。

在 xv6 中存在两个不同但相关的中断概念:

1. 当前中断状态#

  • 通过 intr_get() 函数检查
  • 表示中断是否当前实际处于启用状态

2. 中断启用历史#

  • 存储在 mycpu()->intena
  • 表示在最外层调用 push_off() 之前中断是否处于启用状态
  • intena 是一个标志位

为什么需要两者#

代码中的注释解释了这一点:

// Saves and restores intena because intena is a property of this
// kernel thread, not this CPU. It should be proc->intena and proc->noff, 
// but that would break in the few places where a lock is held but
// there's no process.
c

这表明:

  1. intena 记录的状态属于内核线程(进程),而不是 CPU 硬件
  2. 每次上下文切换需要保留这个值,以便进程下次运行时恢复正确的状态

具体工作流程#

1. 检查中断是否关闭:#

if(intr_get())
  panic("sched interruptible");
c

这确保在上下文切换期间中断是实际禁用的,防止切换过程被打断

2. 保存中断启用历史:#

intena = mycpu()->intena;
c

这保存了进程在最初禁用中断前的状态(由 push_off()/pop_off() 维护)

3. 上下文切换:#

swtch(&p->context, &mycpu()->context);
c

切换到调度器,可能长时间不返回

4. 恢复中断启用历史:#

mycpu()->intena = intena;
c

当进程再次被调度时,恢复原来的中断历史状态

具体案例说明#

考虑这种情况:

  1. 进程 P1 原本中断是启用的
  2. P1 调用 acquire() 获取锁,其中调用 push_off() 禁用中断,并记录 mycpu()->intena = 1
  3. P1 执行 yield(),进入 sched()
  4. sched() 保存 intena=1,切换到调度器
  5. 很长时间后,P1 再次被调度
  6. 此时需要恢复 mycpu()->intena = 1,表示”此进程最初是启用中断的”
  7. 当 P1 完成工作并释放锁时,pop_off() 会根据 intena=1 重新启用中断

如果不保存和恢复 intena,P1 重新运行时将”忘记”它原本应该启用中断。

总结#

  • 检查中断关闭:是安全性检查,确保上下文切换过程不会被打断
  • 保存 intena:是状态保存,确保进程重新运行时能正确恢复中断历史状态

这种设计反映了 xv6 操作系统对并发和中断处理的精心设计,确保了在多处理器环境下的正确行为,同时维护了每个内核线程的中断期望状态。

xv6 中的两个中断相关概念
https://www.lunarain.top/blog/%E4%B8%AD%E6%96%AD%E6%9C%BA%E5%88%B6
Author Lunarain_079
Published at April 8, 2025
Comment seems to stuck. Try to refresh?✨