XV6的锁
简介
用自己的语言描述xv6的锁。
锁不是操作系统的某个子系统,感觉它更像一个工具,是给并发加上了镣铐。在请求锁acquire()
和释放锁release()
之间的代码是临界区(critical section),以前以为临界区是个空间的概念,现在看来临界区也是时间的概念。因为在持有锁的这段时间并没有说不能做什么事情,只是大家都在这个时间段里保护某个数据结构的不变性而已。
引子
自旋锁的核心数据结构是struct spinlock
,核心操作是acquire()
和release()
,它们定义在kernel
目录下的spinlock.h
和spinlock.c
。
自旋锁
acquire()
用一个while
循环来请求锁,它一直在这个while
上自旋直到获得锁。获得锁的方式是用builtin函数__sync_lock_test_and_set
,这个函数使用原子指令amoswap
使锁的值变为1。release()
使用__sync_lock_release
来释放锁,这个函数最终也使用了amoswap
指令。
自旋锁是要一直死等的,它的代价挺大的,感觉还是能不用尽量不用的好。必须要用的时候,应使临界区尽量地小。且被保护的数据结构在所有的地方都要加自旋锁,否则就失去了保护的意义。
当自旋锁之间存在次序的关系时,所有的代码都必须遵循同样的次序来持有和释放锁。
当在线程里使用锁的时候,如果发生中断,中断代码如请求同样的锁会发生死锁。所以,在请求锁的时候要禁止中断,在释放锁的时候再恢复中断。这就是push_off
和pop_off
做的事。
编译器有的时候不按代码的次序安排指令,这对于锁来说是有害的,所以需要__sync_synchronize
来建立内存屏障,使load和store指令不要跨屏障使用。