# 7.10 练习

1. `Sleep `必须检查`lk != &p->lock`来避免死锁(***kernel/proc.c***:558-561). 假设通过将

```c
if(lk != &p->lock) {
  acquire(&p->lock); 
  release(lk); 
}
```

替换为

```c
release(lk); 
acquire(&p->lock);
```

来消除特殊情况，这样做将会破坏`sleep`。是如何破坏的呢？

1. 大多数进程清理可以通过`exit`或`wait`来完成。事实证明，必须是`exit`作为关闭打开的文件的那个。为什么？答案涉及管道。
2. 在xv6中实现信号量而不使用`sleep`和`wakeup`（但可以使用自旋锁）。用信号量取代xv6中`sleep`和`wakeup`的使用。判断结果。
3. 修复上面提到的`kill`和`sleep`之间的竞争，这样在受害者的`sleep`循环检查`p->killed`之后但在调用`sleep`之前发生的`kill`会导致受害者放弃当前系统调用。
4. 设计一个计划，使每个睡眠循环检查`p->killed`，这样，例如，virtio驱动程序中的一个进程可以在被另一个进程终止时从while循环快速返回。
5. 修改xv6，使其在从一个进程的内核线程切换到另一个线程时仅使用一次上下文切换，而不是通过调度器线程进行切换。屈服（yield）线程需要选择下一个线程本身并调用`swtch`。挑战在于：防止多个内核意外执行同一个线程；获得正确的锁；避免死锁。
6. 修改xv6的调度程序，以便在没有进程可运行时使用RISC-V的`WFI`（wait for interrupt，等待中断）指令。尽量确保在任何时候有可运行的进程等待运行时，没有核心在`WFI`中暂停。
7. 锁`p->lock`保护许多不变量，当查看受`p->lock`保护的特定xv6代码段时，可能很难确定保护的是哪个不变量。通过将`p->lock`拆分为多个锁，设计一个更清晰的计划。
