Linux内核同步(一):互斥锁与自旋锁详解
1. 内核锁的基本类型
Linux内核主要有两种锁:互斥锁(mutex lock)和自旋锁(spinlock),实际上还有其他类型的锁以及同步技术。内核文档中提到内核实现了三种锁类别:睡眠锁、CPU本地锁和自旋锁。睡眠锁包括(RT)互斥锁、信号量等;自旋锁包括自旋锁、读写自旋锁等;“本地锁”通常用于实时(RT)场景,也可用于锁调试。
2. 互斥锁与自旋锁的概念区别
假设有三个线程tA、tB和tC在SMP系统上并行运行,为解决并发问题,在临界区开始前获取锁,结束后释放锁。当三个线程同时尝试获取锁时,锁的API语义保证只有一个线程能成功获取。假设tB获取到锁,成为“赢家”线程,tA和tC则成为“输家”线程。
- 互斥锁 :输家线程会进入睡眠状态。当赢家线程解锁后,内核会唤醒所有输家线程,它们再次竞争锁。因此,互斥锁也被称为睡眠锁。
- 自旋锁 :输家线程不会睡眠,而是通过“自旋”等待锁被释放。概念上可以表示为
while (locked) ;
,但实际实现并非如此简单。自旋锁主要用于多核(SMP)系统,当赢家线程在临界区执行时,输家线程在其他CPU核心上自旋等待。现代自旋锁的实现代码经过高度优化,例如ARM的自旋锁实现可能使用等待事件(WFE)机器语言指令,使CPU在低功耗状态下等待。
3. 理论上如何选择锁
自旋锁的开销比互斥锁小。互斥锁的输家线程需要睡眠和唤醒,这涉及到上下文切换。上下文切换的最小“成本”是两次上下文切换