Linux内核中的锁

1         锁的分类

Linux内核中锁可以分为三大类

睡眠锁

CPU本地锁

自旋锁

睡眠锁作用

睡眠锁只能在可抢占任务的上下文中使用。睡眠锁可详细划分为以下几类:

mutex

rt_mutex

semaphore

rw_semaphore

ww_mutex

percpu_rw_semaphore

rt_mutex

rt_mutex是支持优先级继承(PI)的互斥锁,在未启用PREEMPT_RT 实时补丁的内核上,PI 机制会受抢占机制和关中断代码段的限制而无法充分发挥作用。

semaphore

semaphore 是一种计数信号量实现。信号量通常用于序列化和等待。

rw_semaphore

rw_semaphore 是一种多读者单写者锁定机制。

在非 PREEMPT_RT 内核上,该实现是公平的,因此可以防止写者饥饿。

rw_semaphore 默认情况下符合严格的所有者语义,但存在允许非所有者为读者释放的专用接口

CPU本地锁

CPU本地锁为local_lock,在非实时内核(不支持PREEMPT_RT)时,local_lock是对中断禁用和抢占经用的的封装。中断禁用和抢占禁用只影响本地CPU,不适用于多CPU之间的并发控制。

在非PREEMPT_RT内核上,local_lock 操作映射到抢占和中断禁用和启用原语,详细映射如下表所示:

内核开启PREEMPT_RT后,local_lock映射到每个 CPU 的 spinlock_t,因此所有的spinlock_t都适用于local_lock

保护针对每个CPU的数据时,如果通过关闭中断或禁用任务抢占的方式实现并发控制时,可以优先使用local_lock。

自旋锁

自旋锁主要分

raw_spinlock_t

位自旋锁

在非实时内核中,如下两类也属于自旋锁。

spinlock_t

rwlock_t

自旋锁隐式的禁用任务抢占。


自旋锁的使用

在linux内核实现中,如果共享数据被进程上下文和中断上下文同时访问该如何保护,如果只是进程上下文的访问,可以考虑使用mutex或semaphore,但如果中断上下文也参与进来,则需要使用自旋锁对共享数据进行保护。

在中断上下文中,是不允许睡眠的,此时如果需要使用锁,则需要使用非睡眠锁(自旋锁)。

自旋锁不可以递归使用。

自旋锁的死锁:

         某线程获取了锁,进入临界区处理,但被中断处理程序打断,中断处理程序也获取该锁进行处理(此时中断处理程序无法获取,只能自选),此时无法获取锁,中断无法退出,导致后续线程中释放锁的代码也无法被执行,从而造成死锁。

自旋锁解决的几种资源使用场景

场景一:抢占式内核中考虑如下场景是否会造成冲突。

进程A通过系统调用访问共享资源R。

进程B通过系统调用访问共享资源R。

对R资源不加锁时的访问:

假设A在访问资源R的过程中发生中断,中断唤醒了优先级更高的进程B,在中断返回时,调度了B,如果不进行资源保护,则存在进程A与进程B同时访问R资源,导致程序执行结果异常。

对R资源加自旋锁保护后的访问:

进程A在访问资源R前获取了锁spinlock,在使用R资源时,发生中断,中断唤醒了优先级更给高的进程B,B在访问资源R时尝试获取锁spinlock,但由于锁已经被A获取,因此B

永久的进入自选状态,从而造成异常。linux内核对这种状况的处理很简单,即A在获取自旋锁时,禁用本地CPU的任务抢占,如果进程A与进程B运行在同一个CPU上,则进程A不会被进程B抢占,中断返回时,唤醒的任务仍然是进程A;如果进程A与进程B运行在不同的CPU上,进程B在另外一个CPU上自选等待,进程A在当前CPU上执行释放锁即可以。

场景二:考虑中断上下文也需要访问共享资源R

进程A需要通过系统调用访问共享资源R。

某个外设中断处理也需要访问共享资源R。

在这样的场景下,使用自旋锁保护资源R会发生什么。

此时A获取锁进入资源临界区访问R,此时发生中断,在中断处理过程中,需要获取自旋锁访问R,由于锁已经被进程A获取,中断处理程序尝试获取锁无法获取到,从而产生异常。

对于以上这种场景,如果资源涉及到进程上下文和中断上下文同时使用,则该自旋锁需要禁用本CPU中断处理

raw_spinlock_t

raw_spinlock_t是所有内核(包括 PREEMPT_RT 内核)中的严格自旋锁实现。 仅在真正的关键核心代码、低级中断处理以及需要禁用抢占或中断(例如,安全访问硬件状态)的地方使用 raw_spinlock_t。

spinlock_t

spinlock_t 的作用随着 PREEMPT_RT 的状态而变化。在非 PREEMPT_RT 内核上,spinlock_t 映射到 raw_spinlock_t 并且具有完全相同的语义。

锁的嵌套注意事项

最基本的规则是

相同锁类别(睡眠、CPU 本地、自旋)的锁类型可以任意嵌套,只要它们遵守常规锁排序规则以防止死锁即可。

睡眠锁类型不能嵌套在 CPU 本地和自旋锁类型中。

CPU 本地和自旋锁类型可以嵌套在睡眠锁类型中。

自旋锁类型可以嵌套在所有锁类型中

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容