实验七 死锁与简单处理
当多个任务访问同一个资源(数据)是就会引发竞争条件问题,这不仅在进程间会出现,在操作系统和进程间也会出现。由竞争条件引发的问题很难复现和调试,这也是其最困难的地方。本实验的目的在于了解竞争条件和死锁现象,并掌握处理这些问题的初步方法等。
死锁的出现
只需要在在 main.rs 里的 not_main 函数里循环地调用 print! 宏就极容易出现死锁。
#[no_mangle]
pub extern "C" fn not_main() {
// ...
println!("It did not crash!");
loop {
print!("-");
}
// ...
}
下表展示了我们系统(当前状态)如果在 not_main 函数和中断处理函数中都调用打印宏(println!)可能出现死锁的情况。 not_main 函数与异常处理函数(handle_timer_irq)之间因为竞争资源而可能出现死锁。这是因为 not_main 函数占有WRITE锁且请求在CPU上运行,而中断处理函数占有CPU请求WRITE锁,构成死锁。
Timestep |
not_main |
handle_timer_irq |
0 |
calls print! |
|
1 |
print locks WRITER |
|
2 |
interrupt occurs, handler begins to run |
|
3 |
calls print! |
|
4 |
print tries to lock WRITER (already locked) |
|
5 |
print tries to lock WRITER (already locked) |
|
… |
… |
|
never |
unlock WRITER |
死锁的简单处理
为了防止出现死锁,一个简单的办法是在使用锁时禁止中断。但需要注意的是禁用中断会增加中断响应延迟,而中断响应延迟一个非常重要的性能指标。所以只能在短时间内禁用中断。
在 uart_console/mod.rs 中的_print 中关闭中断,然后再打开中断。
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use core::fmt::Write;
unsafe {
// 关闭d a i f类型的中断
asm!("msr daifset, #0xf");
}
WRITER.lock().write_fmt(args).unwrap();
unsafe {
// 仅打开i类型的中断,不支持嵌套,嵌套应该保存状态,然后再恢复之前的状态
asm!("msr daifclr, #2");
}
}