实验七 死锁与简单处理

当多个任务访问同一个资源(数据)是就会引发竞争条件问题,这不仅在进程间会出现,在操作系统和进程间也会出现。由竞争条件引发的问题很难复现和调试,这也是其最困难的地方。本实验的目的在于了解竞争条件和死锁现象,并掌握处理这些问题的初步方法等。

死锁的出现

只需要在在 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");
    }

}