[001]从操作系统层面看锁的逻辑

从操作系统层面,锁 (Lock) 是一种同步机制,用于控制多个线程或线程对共享资源的访问,防止竞态条件(race condition).常见的锁包括互斥锁(mutex)、读写锁(read-write lock)、自旋锁(spinlock)等。下面逐一分析其原理,从内核实现,CPU指令,调度机制等角度解释。

1.互斥锁(Mutex)

原理

作用:确保同一时刻只有一个线程/进程访问临界区。

实现机制:基于原子操作和睡眠/唤醒机制。

操作系统实现细节

1.用户态尝试加锁(快速路径):

通常用 原子CAS (Compare-And-Swap) 尝试将锁状态从"未占用"变为已经占用。

成功则进入临界区,失败则进入下一步。

  1. 内核态挂起(慢路径):

如果锁被占用,则线程被阻塞,进入等待队列(入Linux的futex队列)

等待唤醒时才尝试重新加锁。

3.解锁时唤醒等待线程

解锁线程将锁状态置为 未占用 并唤醒一个或者多个等待线程。

Linux示例:

Linux 中pthread_mutex实际使用了futex(fast userspace mutex)

futex 运行用户态,快速获取锁,失败时才陷入内核挂起,避免频繁陷入内核态。

二、自旋锁(Spinlock )

原理

不会挂起线程,而是获取不到锁时,持续循环检查锁状态("忙等待")

适用于临界区非常短、线程不会被长时间阻塞的场景(如内核中断上下文)

实现方式

使用原子质量(如xchg,cmpxhcg)实现加锁

CPU指令层级提供如LOCK CMPXCHG来保证总线原子性。

缺点

占用CPU资源,等待时间线程无法做其他工作。

多核系统上会导致缓存一直性流量(chache coherence traffice)增加。

三、读写锁(Read-Write Lock)

原理

允许多个读者共享访问,但写者必须独占。

优化了读多写少的场景。

实现机制

通常维护一个计数器记录读者数量。

写线程必须等待所有读者释放锁后才能进入。

内核或者用户态原子操作与条件变量管理状态

操作系统支持

POSIX提供pthread_rwlock ,Linux 内部实现使用类似rw_semaphore.

四、信号量(Semaphore)

原理

计数信号量可控制对资源的访问数量

二值信号量可以作为互斥锁的替代。

实现机制

内核维护一个计数器,P (wait) 操作将其减一,若<0则阻塞;V(signal )操作将其加一,唤醒等待线程

五、实现关键点:从硬件到内核

层级 关键点
CPU 提供原子操作:CMPXCHG,XCHG,LL/SC (ARM/MIPS)
缓存一致性 确保多核之间对锁变量的访问保持一致(MESI协议)
内核调度器 维护等待队列,挂起线程,唤醒线程
系统调用 用户态锁失败后,通过futex进入内核挂起

六、总结与对比

锁类型 是否阻塞线程 性能 场景
互斥锁 较好 通用
自旋锁 否(忙等) 非常高(短临界区) 内核、无阻塞上下文
读写锁 优于互斥(读多) 多读少些场景
信号量 适中 计数共享资源控制
相关推荐
期待のcode12 分钟前
MyBatis框架—延迟加载与多级缓存
java·数据库·后端·缓存·mybatis
老华带你飞19 分钟前
小区服务|基于Java+vue的小区服务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·小区服务管理系统
柯南二号1 小时前
【Java后端】MyBatis 和 MyBatis-Plus (MP) 的区别
java·数据库·tomcat
C++chaofan1 小时前
游标查询在对话历史场景下的独特优势
java·前端·javascript·数据库·spring boot
小蒜学长1 小时前
springboot房地产销售管理系统的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端
0wioiw01 小时前
PostgreSQL(②基础命令)
数据库·postgresql
xcLeigh2 小时前
KingbaseES数据库:兼容 SQL 语法及 Oracle 过程化语言的语法基础
数据库
FinTech老王2 小时前
一场“无感换心”手术:金仓数据库如何让电子证照系统平滑告别MongoDB
数据库·mongodb
周杰伦的稻香2 小时前
MySQL中的空间碎片率计算分析
android·数据库·mysql
重启的码农2 小时前
kv数据库-leveldb (13) 缓存 (Cache)
数据库