Appearance
锁和中断处理程序
一些 xv6 自旋锁保护由线程和中断处理程序共同使用的数据。例如,clockintr
时钟中断处理程序可能在内核线程在 sys_sleep
中读取 ticks
的大约同一时间递增 ticks
。锁 tickslock
序列化了这两个访问。
自旋锁和中断的交互带来了一个潜在的危险。假设 sys_sleep
持有 tickslock
,并且它的 CPU 被一个时钟中断打断。clockintr
会尝试获取 tickslock
,看到它被持有,然后等待它被释放。在这种情况下,tickslock
将永远不会被释放:只有 sys_sleep
可以释放它,但 sys_sleep
在 clockintr
返回之前不会继续运行。所以 CPU 将会死锁,任何需要这两个锁的代码也会冻结。
为了避免这种情况,如果一个自旋锁被中断处理程序使用,一个 CPU 绝不能在启用中断的情况下持有该锁。Xv6 更为保守:当一个 CPU 获取任何锁时,xv6 总是禁用该 CPU 上的中断。中断仍然可能在其他 CPU 上发生,所以一个中断的 acquire
可以等待一个线程释放一个自旋锁;只是不能在同一个 CPU 上。
当一个 CPU 不持有自旋锁时,Xv6 会重新启用中断;它必须做一些簿记工作来处理嵌套的临界区。 acquire
调用 push_off
, release
调用 pop_off
来跟踪当前 CPU 上锁的嵌套级别。当该计数达到零时,pop_off
会恢复最外层临界区开始时存在的中断启用状态。intr_off
和 intr_on
函数执行 RISC-V 指令来分别禁用和启用中断。
acquire
在设置 lk->locked
之前严格调用 push_off
是很重要的。如果这两者颠倒了,就会有一个很短的时间窗口,当锁被持有时中断是启用的,一个不幸的定时中断将会使系统死锁。类似地,release
仅在释放锁之后才调用 pop_off
也是很重要的。