MIT 6.1810: xv6 book Chapter7: Locking 笔记

Races

竞争races 发生在同一个内存位置被并发访问,并且其中至少一个访问是写入

竞争的结果取决于编译器生成的机器码,两个CPU介入的时间,内存系统组织内存操作的方式

常见消除竞争的方式是使用锁Lock ,保证 互斥mutual exclusion

锁在保证正确性时,本质上限制了性能

复杂内核为了在追求并发性的同时避免锁的竞争,会设计复杂的数据结构

e.g. 为每个CPU维护一个单独的free list用于kalloc() kfree()

Code: Locks

❌如果我们用C语言写常规的acquire实现代码,两个CPU仍有可能同时运行到if判断语句并同时持有锁

c 复制代码
void
acquire(struct spinlock *lk) // ❌does not work!
{
	for(;;) {
		if(lk->locked == 0) {
			lk->locked = 1;
			break;
		}
	}
}

✅RISC-V提供了原子操作amoswap r, a. amoswap(atomic memory swap),将地址a读取的值与寄存器r交换,返回交换前地址a读取的值

xv6调用了__sync_lock_test_and_set(addr, value)这样我们就把上面的for循环代码写成

c 复制代码
while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
	;

同样地,xv6通过调用__sync_lock_release,用amoswap实现原子操作释放锁

Code: Using locks

决定如何使用锁的准则:

1.任何时候一个CPU写入一个变量同时另一个CPU读写该变量

2.维护共享数据结构的不变性:用一把锁保护所有相关数据,保证锁被释放时不变式内部已完全更新

许多单核处理器操作系统仅使用一个锁

每次从用户空间通过系统调用或中断进入内核态时acquire,返回时release

高效但牺牲并行性

下图展示了xv6中所有的锁

Deadlock and lock ordering

为避免死锁,所有持有多个锁的代码都应该以相同顺序acquire locks

e.g. 文件系统代码在创建文件时,需要

目录锁 -> 新文件inode锁 -> 磁盘块缓冲区锁 -> 磁盘驱动的vdisk_lock -> 进程的p->lock

如果一个CPU在它已经持有该锁的情况下再次申请锁

部分操作系统允许这种情况,称为re-entrant / recursive

xv6认为出现这种情况的原因是操作可能被暂时破坏了且不变式没有被恢复,此时再次申请锁会引发更多bug,并禁止了这种操作

Locks and interrupts

一种死锁的情况是,CPU在持有锁的时候,中断发生且请求该锁

为避免这种情况,如果中断处理函数用到一个锁,CPU永远不在允许中断时持有该锁

xv6的实现方式:CPU持有任何锁时,不允许CPU上发生中断。xv6为当前CPU记录临界区域的嵌套层数,acquire在设置lk->locked前push_off使计数+1,release在设置lk->locked后pop_off使计数-1,并在最外层嵌套区域前后执行intr_off和intr_on开启和关闭中断

Instruction and memory ordering

编译器和CPU可能会优化代码打乱原本代码的执行顺序

xv6为避免出现该情况,在acquire和release的实现中调用了 __sync_synchronize(),它作为内存屏障memory barrier,告诉编译器和CPU不要在优化时将store和load操作越过该屏障

Sleep locks

操作系统中某些锁可能被持有很长时间(e.g. 文件系统),我们要允许进程在长时间循环执行请求操作时让出CPU

xv6为此实现了可睡眠锁sleep-locks ,在等待时让出CPU

因为可睡眠锁会允许中断,它不能用在中断处理函数中;可睡眠锁会让出CPU,它不能用在spinlock的临界区域中

sleep-lock & spinlock

sleep-lock用于长时间的操作

spinlock适合短的临界区域

Real world

大多数操作系统支持 POSIX threads(Pthreads) ,允许一个用户进程在不同CPU上运行多个线程

另外,多个CPU在请求同一个锁时开销会很大,为此,许多操作系统实现了不需要锁的数据结构和算法

相关推荐
RainCity4 天前
Java Swing 自定义组件库分享(十二)
java·笔记·后端
LinXunFeng11 天前
Obsidian - 使用 Share Note 分享笔记并自部署
前端·笔记·github
闪闪发亮的小星星15 天前
高斯光以及高斯光公式解释
笔记
cqbzcsq16 天前
CellFlow虚拟细胞论文阅读
论文阅读·人工智能·笔记·学习·生物信息
阿米亚波16 天前
【Windows】QEMU 启动 openEuler aarch64/arm64 架构系统 + 离线软件源
linux·windows·经验分享·笔记·架构·arm
自传.16 天前
尚硅谷 Vibe Coding|第三章(1) Claude Code深度使用与进阶技巧 学习笔记
笔记·学习·尚硅谷·vibecoding
.千余16 天前
【C++】模板进阶全解:非类型参数|全特化|偏特化|分离编译完全指南
开发语言·c++·笔记·学习·其他
自传.16 天前
尚硅谷 Vibe Coding|第二章 AI编程工具生态 学习笔记
笔记·学习·ai编程·尚硅谷·vibe coding
秋波。未央16 天前
Java Agent 开发 · Day 1 学习笔记(含作业完整标准答案)
java·笔记·学习
中屹指纹浏览器16 天前
2026指纹浏览器字体指纹、字体渲染偏差检测与全维度虚拟字体池搭建方案
经验分享·笔记