在理解Linux内核中的spin_lock锁之前,先看看内核中主要的对spin_lock的支持文件 include/linux/spinlock.h 锁通用层定义,可以认为其是一个框架层。主要是通用的spinlock锁和rwlock锁的申明。以及锁机制提供的API。对于锁来说,其主要是用于 数据的同步。 在单处理器上锁实现较为简单,严格说,在单处理器上,如果内核不支持抢占,包括用户空间抢占和内核空间抢占,那么单处理器上 不存在竞争关系,只要任务处于当前运行状态,其必然能获取锁。否则就是程序设计不合理。因为在单处理器上,当前只有一个可以运行的任务, 这个任务如果获取不到锁,或者认为当前任务不能对资源独占使用,那么持有锁的任务,你在脱离CPU时候为啥不放弃锁,还让别人等待呢?而你又 持有到什么时候,又让别人等待多久呢?所以,单处理器并且不支持抢占时,任务持有锁时,必须在由运行到非运行状态释放锁。否则导致其他任务 不可能使用资源。 而对于单处理器支持抢占情形,这种是内核支持用户空间抢占和内核空间抢占。一般来说用户空间抢占都是支持的。内核空间抢占需要仔细考量的 。在这个时候,持有锁的任务正常运行时,和上面讨论一致。但关键是持有锁的任务在被抢占时情形,也就是那些牛逼的进程需要抢占当前进程。 这个时候,我们需要考虑抢占是发生在用户空间还是内核空间以及能否安全抢占。因为我们支持抢占,其实发生在内核空间和用户空间已经无关紧要。 我们只需要考虑那些牛逼的任务抢占当前任务是否安全,而不需要考虑其用户态还是内核态。我们如何知道抢占安全呢,Linux内核设计者考虑在 thread_info中设计一个preempt_count成员,其用于标记是否能抢占,也就是抢占当前进程。当我们持有锁时,把preempt_count计算增加,以表示 当前我们持有锁,你其他牛逼的进程不能抢占当前的运行的任务。如果当前进程没有持有锁,preempt_info值为0,这时其他进程可以抢占当前运行的 任务。可以看到,单处理器上支持抢占的,处理稍许复杂了些。 对于真正的并发来说,即对于多核系统来说,上面同时运行多个任务,这是真正的并发。任务任何时候都可能投入运行,也可能在任务时候脱离 CPU。此时,对于持有锁的任务来说,一个方面其不能被抢占。当然你如果不持有锁,抢占不抢占也无所谓了。另一方面,对于想获取锁的任务来说, 它只能执行等待。这个等待操作发生在其他处理器上,也就是持有锁进程运行时,等待可能发生在其他核上了。对于想获取锁的任务来说,你要么执行 所谓的忙等待,一直循环;要么执行一个sleep,等持有锁任务释放锁时任务被唤醒,再去获取锁,开始自己的工作。 所以对于多处理器系统来说,其他核上的任务可能确实发生跟本cpu抢占锁的情形。如果支持抢占,其他核上的高优先任务确实可以抢占,但仍需要检查 抢占环境是否安全。但此时机制与单处理器支持抢占类似。 在Linux上,内核区分单处理器还是多处理器。单处理时,锁直接退化为空操作。 SMP系统: asm/spinlock_types.h: 这个是体系结构提供的,包括arch_spinlock_t/arch_rwlock_t以及锁初始化initializers asm/spinlock.h: 这个也是体系架构对锁实现的增强。包括arch_spin_*()/etc.低级别层次的实现,可能很多都是汇编代码。
linux/spinlock_types.h: 定义锁通用类型和简单初始化宏以及函数,这个文件会包含asm下面的asm/spinlock_types.h
linux/spinlock_api_smp.h: 包括一些_spin_*() APIs的原型。 linux/spinlock.h: 最终的spin_*() APIs申明。这个文件会包含asm/spinlock.h。 UP处理器:
linux/spinlock_type_up.h:包括通用的原型申明,这些可能为NULL,如果是non-debug版本 linux/spinlock_types.h: 定义锁通用类型和简单初始化宏以及函数 linux/spinlock_up.h: 包括arch_spin_*()/etc. 这些可能为空操作,如果non-debug和non-preempt。 单处理器上也会提高这些: linux/spinlock_api_up.h: 提供_spin_*() APIs申明。 linux/spinlock.h: 提供最终的spin_*() APIs接口。
上面这些文件需要注意,一般linux/xx.h这个文件会包括asm/xx.h这个文件。也就是说,linux/xx.h是整个API接口层。 而asm-generic/xx.h一般是内核针对各个体系架构抽象出的公共部分。
我们在内核开发过程中,直接使用linux/xx.h就可以了。一般不需要asm/xx.h。除非你BSP开发中自己定义了一套API,那你还是asm/xx.h给包括进来吧
内核对于spinlock实现代码位于kernel/locking目录下,目录下makefile如下:
obj-$(CONFIG_SMP) += spinlock.o obj-$(CONFIG_LOCK_SPIN_ON_OWNER) += osq_lock.o obj-$(CONFIG_PROVE_LOCKING) += spinlock.o obj-$(CONFIG_QUEUED_SPINLOCKS) += qspinlock.o obj-$(CONFIG_RT_MUTEXES) += rtmutex.o obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o obj-$(CONFIG_QUEUED_RWLOCKS) += qrwlock.o obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o
相关的config配置: CONFIG_DEBUG_SPINLOCK CONFIG_GENERIC_LOCKBREAK CONFIG_PROVE_LOCKING CONFIG_SMP