在本作业中,将探索中断和锁的相互作用。
确保自己理解如果xv6内核执行下列代码碎片会发生什么:
struct spinlock lk;
initlock(&lk, "test lock");
acquire(&lk);
acquire(&lk);
用一句话介绍发生了什么:
会产生panic("acquire")。
Interrupts in ide.c
acquire确保在本地处理器上通过cli指令(通过pushcli())关闭中断。并且这个中断一直保持关闭状态直到处理器拥有的最后一个锁被释放。(此时他们能使用sti)
当持有ide锁时,让我们看一下如果打开中断会怎么样。在ide.c文件中的iderw,在acquire()后添加一个对sti()的调用,在release()之前添加一个对cli()的调用。重新编译内核并且在QEMU中启动。
内核很可能会在启动后很快panic,如果没有尝试引导QEMU几次。
结果:
80104431: eb 0d jmp 80104440 <release>
80102093: 8b 1d 64 a5 10 80 mov 0x8010a564,%ebx
80105955: e9 cd fe ff ff jmp 80105827 <trap+0x57>
8010570c: 83 c4 04 add $0x4,%esp
80100183: 83 c4 10 add $0x10,%esp
801019da: 8b 5d e4 mov -0x1c(%ebp),%ebx
bp = bread(ip->dev, bmap(ip, off/BSIZE));
80101be2: 83 c4 10 add $0x10,%esp
80101d4b: 83 c4 10 add $0x10,%esp
80101ef3: c9 leave
80100a37: 83 c4 10 add $0x10,%esp
我理解的发生这种错误的原因可能是(不一定正确),在一个acquire关闭中断之后发生了进程切换,另一个acquire执行并获得自选锁,之后,又打开了中断,中断处理程序恢复到原来的状态,开始去尝试获得自选锁,此时产生了panic。
寄存器没有看得懂...
Interrupts in file.c
现在让我们看看如果我们在持有file_table_lock的同时打开中断会发生什么。 此锁保护文件描述符表,当应用程序打开或关闭文件时,内核会对其进行修改。 在file.c
中的filealloc()
中,在调用acquire()
之后添加对sti()
的调用,并在每个release()
之前添加一个cli()
。 您还需要在其他#include行之后的文件顶部添加#include "x86.h"
。 重建内核并在QEMU中启动它。 它很可能不会panic
。
用几句话解释为什么内核没有panic。为什么文件表锁和ide锁在这方面有不同的行为?
因为相对于ide锁,内核进行打开或关闭文件操作的次数和时间都非常少,基本不可能同时对两个文件进行读写,因此发生冲突的可能性也很小。
xv6 lock implementation
Why does release() clear lk->pcs[0] and lk->cpu before clearing lk->locked? Why not wait until after?
总结
也不知道这些回答对不对...甚至还有一些没答出来,以后再整理...