In continuation of the previous text第5章:并发与竞态条件-6:Completions, let's GO ahead.
Spinlocks
Semaphores are a useful tool for mutual exclusion, but they are not the only such tool provided by the kernel. Instead, most locking is implemented with a mechanism called a spinlock. Unlike semaphores, spinlocks may be used in code that cannot sleep, such as interrupt handlers. When properly used, spinlocks offer higher performance than semaphores in general. They do, however, bring a different set of constraints on their use.
信号量是互斥的实用工具,但并非内核提供的唯一此类工具。实际上,大多数锁机制由一种名为自旋锁(spinlock) 的机制实现。与信号量不同,自旋锁可用于不可睡眠的代码(如中断处理程序)。使用得当的情况下,自旋锁的性能通常优于信号量,但也带来了一系列不同的使用约束。
Spinlocks are simple in concept. A spinlock is a mutual exclusion device that can have only two values: “locked” and “unlocked.” It is usually implemented as a single bit in an integer value. Code wishing to take out a particular lock tests the relevant bit. If the lockis available, the “locked” bit is set and the code continues into the critical section. If, instead, the lockhas been taken by somebody else, the code goes into a tight loop where it repeatedly checks the lock until it becomes available. This loop is the “spin” part of a spinlock.
自旋锁的概念很简单:它是一种互斥设备,仅有两种状态 ——“锁定(locked)” 和 “未锁定(unlocked)”,通常由整数中的一个比特位实现。想要获取锁的代码会检查对应的比特位:若锁可用,则置位 “锁定” 比特并进入临界区;若锁已被其他线程持有,则代码进入忙循环(tight loop),反复检查锁的状态,直到锁变为可用 —— 这个循环就是自旋锁中 “自旋(spin)” 的由来。
Of course, the real implementation of a spinlockis a bit more complex than the description above. The “test and set” operation must be done in an atomic manner so that only one thread can obtain the lock, even if several are spinning at any given time. Care must also be taken to avoid deadlocks on hyperthreaded processors— chips that implement multiple, virtual CPUs sharing a single processor core and cache. So the actual spinlockimplementation is different for every architecture that Linux supports. The core concept is the same on all systems, however, when there is contention for a spinlock, the processors that are waiting execute a tight loop and accomplish no useful work.
当然,自旋锁的实际实现比上述描述复杂一些:
-
“测试并置位(test and set)” 操作必须以原子方式执行,确保即使多个线程同时自旋,也只有一个线程能获取锁;
-
需特别避免超线程处理器(单个核心和缓存共享多个虚拟 CPU 的芯片)上的死锁问题。
因此,Linux 支持的每种架构都有不同的自旋锁实现,但核心思想一致:当锁存在竞争时,等待的处理器会执行忙循环,无法完成任何有效工作。
Spinlocks are, by their nature, intended for use on multiprocessor systems, although a uniprocessor workstation running a preemptive kernel behaves like SMP, as far as concurrency is concerned. If a nonpreemptive uniprocessor system ever went into a spin on a lock, it would spin forever; no other thread would ever be able to obtain the CPU to release the lock. For this reason, spinlock operations on uniprocessor systems without preemption enabled are optimized to do nothing, with the exception of the ones that change the IRQ masking status. Because of preemption, even if you never expect your code to run on an SMP system, you still need to implement proper locking.
自旋锁本质上是为多处理器(SMP)系统设计的,但支持抢占的单处理器工作站在并发特性上与 SMP 系统类似。若非抢占式单处理器系统进入锁自旋,会陷入无限循环 —— 没有其他线程能获取 CPU 来释放锁。
因此,未启用抢占的单处理器系统中,自旋锁操作会被优化为 “无操作”(仅改变中断屏蔽状态的操作除外)。但由于抢占机制的存在,即使你的代码永远不会运行在 SMP 系统上,也仍需实现正确的锁机制。
补充说明:
-
自旋锁的核心约束:不可睡眠
持有自旋锁时,线程会持续占用 CPU(自旋),若此时睡眠(如调用
kmalloc(GFP_KERNEL)、wait_event),会导致其他等待锁的线程无限自旋,浪费 CPU 资源,甚至引发死锁。因此,自旋锁仅适用于中断上下文或进程上下文但无睡眠操作的场景。 -
与信号量的核心差异
特性
自旋锁
信号量
等待方式
忙循环(自旋)
阻塞睡眠
适用上下文
中断上下文、无睡眠进程上下文
进程上下文(可睡眠)
性能(无竞争)
极高(无上下文切换开销)
较低(可能有切换开销)
竞争代价
CPU 资源浪费(自旋)
低(阻塞不占 CPU)
-
自旋锁的正确使用原则
-
临界区必须极短:自旋锁的竞争代价是 CPU 空转,因此临界区代码需尽可能精简(如仅修改共享变量),避免长时间持有锁;
-
禁用抢占:持有自旋锁时,内核会自动禁用本地 CPU 的抢占(防止同一 CPU 上的其他线程抢占后自旋等待);
-
中断屏蔽:若中断处理程序也会访问同一共享资源,需在获取自旋锁时屏蔽本地中断(如
spin_lock_irqsave),避免中断触发后自旋等待已被占用的锁。
-
-
单处理器系统的自旋锁优化
未启用抢占的单处理器系统中,自旋锁无需实际 “自旋”—— 因为没有其他线程能并发访问临界区,此时自旋锁仅作为 “禁用抢占” 的标记,确保代码执行的原子性。
4309

被折叠的 条评论
为什么被折叠?



