XV6的锁

简介

用自己的语言描述xv6的锁。

锁不是操作系统的某个子系统,感觉它更像一个工具,是给并发加上了镣铐。在请求锁acquire()和释放锁release()之间的代码是临界区(critical section),以前以为临界区是个空间的概念,现在看来临界区也是时间的概念。因为在持有锁的这段时间并没有说不能做什么事情,只是大家都在这个时间段里保护某个数据结构的不变性而已。

引子

自旋锁的核心数据结构是struct spinlock,核心操作是acquire()release(),它们定义在kernel目录下的spinlock.hspinlock.c

自旋锁

acquire()用一个while循环来请求锁,它一直在这个while上自旋直到获得锁。获得锁的方式是用builtin函数__sync_lock_test_and_set,这个函数使用原子指令amoswap使锁的值变为1。release()使用__sync_lock_release来释放锁,这个函数最终也使用了amoswap指令。

自旋锁是要一直死等的,它的代价挺大的,感觉还是能不用尽量不用的好。必须要用的时候,应使临界区尽量地小。且被保护的数据结构在所有的地方都要加自旋锁,否则就失去了保护的意义。

当自旋锁之间存在次序的关系时,所有的代码都必须遵循同样的次序来持有和释放锁。

当在线程里使用锁的时候,如果发生中断,中断代码如请求同样的锁会发生死锁。所以,在请求锁的时候要禁止中断,在释放锁的时候再恢复中断。这就是push_offpop_off做的事。

编译器有的时候不按代码的次序安排指令,这对于锁来说是有害的,所以需要__sync_synchronize来建立内存屏障,使load和store指令不要跨屏障使用。