Linux的内核同步

内核抢占

内核抢占是指在内核态运行时允许高优先级任务中断当前任务执行,从而提升系统响应性和实时性的机制。同样的,内核抢占依赖于两个重要的关键条件:

  • 可重入的内核设计:可重入意味着 内核代码在被抢占后,仍能被其他内核线程或中断安全地再次进入并执行,不会引发数据破坏或逻辑错误
  • 完善的内核同步机制:抢占意味着多个内核任务可能同时执行或交错执行某些共享资源操作,因此必须通过同步手段(如自旋锁、顺序锁、RCU等)来保护临界区。同步的目标是防止竞态条件和保证内存一致性

同步原语

每CPU变量

每CPU变量(Per-CPU variable) 是 Linux 内核中一种优化机制,指的是:为每个 CPU 分配一份独立的变量副本,从而避免多核系统中对共享变量的竞争,提高并发性能。由于使用每CPU变量是为了解决cache热的问题,因此每CPU变量非常适合最终一致性和无共享写的场景,但不适用于要求跨 CPU 严格顺序或同步一致性的场景。每CPU变量常见于计数,如中断计数和网络包计数。

原子操作

​ **原子操作(Atomic Operation)**是指一类 不可中断、不可分割 的操作,他可以保证这个操作要么一定完成,要么不执行。原子操作是一种轻量级、高效的线程同步方式,适合用于简洁的并发控制场景

内存屏蔽

​ 内存屏障(Memory Barrier)是一种指令级的同步机制,用于防止指令在编译器或 CPU 执行阶段发生重排序。在现代编译器中,为了优化代码的执行,会发生指令重排,而内存屏障的作用就是保证在这之后的指令不会越过屏障。

自旋锁

自旋锁是一种 忙等锁,即如果当前锁已被持有,等待者会在原地循环检查(“自旋”),而不是睡眠等待。

在 Linux 内核中,自旋锁常用于短时间的临界区保护,因为其不涉及上下文切换。

  • 抢占式自旋锁:持有自旋锁期间,关闭调度器,当前CPU不会被抢占。其更简单、效率高,适用于SMP系统中频繁使用的场景
    • 非抢占式自旋锁:常见于非SMP系统,在请求锁的期间,允许被抢占,有强的实时性要求系统中是允许自旋锁被抢占

顺序锁

顺序锁是一种允许读者在不加锁的情况下并发读取数据,但需要检测是否发生“写冲突”,如果发生则重新读取。即读不需要获取锁的限制,但是写的时候,不允许读,并且独占资源。由于不对读做限制,在发生写入后,要实时更新,这个时候是根据序列号是否变化来判断的。

  • 读数据的时候:获取序列号,如果为奇数(表明正在写入,不读取)。为偶数,说明可以读,但是要检查序列号是否一样,不一样要重新读取
  • 写数据的时候:获取写的互斥锁,序列号加一,开始写入。当写完之后,序列号在加一,表明写入完成,同时释放写锁。

RCU(READ-COPY-UPDATE)

RCU的核心思想是:将读写并发冲突的问题,通过将“读”与“写”解耦,转化为“复制 + 替换 + 延迟回收”模型来解决,以达到极高性能的并发读访问。

R(Read)——无锁读取

RCU 的“Read”阶段允许多个读者线程在不加锁的情况下并发访问共享数据结构。读者只需通过调用 rcu_read_lock()rcu_read_unlock() 将访问代码包围起来,便可安全读取指针指向的数据,而无需担心数据正在被更新。这种无锁读机制极大地提高了系统在读多写少场景下的并发性能,因为读者不会阻塞写者,也不会相互阻塞。


C(Copy)——复制修改

当需要更新共享数据时,写者不会直接修改正在被读者访问的数据,而是首先复制一份数据副本。在副本上进行修改后,通过原子方式将全局指针指向新的数据副本,完成所谓的“发布更新”操作。这个“Copy”过程确保了读者始终访问一致、不可变的旧数据视图,从而实现读写解耦。


U(Update)——延迟回收

一旦新的数据副本被成功替换,旧数据就不再是当前版本,但它仍可能被一些尚未退出 RCU 读临界区的线程引用。为了安全地回收旧数据,RCU 会等待一个称为“宽限期(Grace Period)”的时间段,在此期间内,系统保证所有正在读取旧数据的线程都已完成访问。只有在宽限期结束后,旧数据才会被安全释放或重用。

信号量

在Linux中,信号量被分为内核信号量和POSIX信号量(用户态)

内核信号量(Kernel Semaphore)

​ 内核信号量是 Linux 内核空间中用于实现线程间同步的同步原语,适用于内核模块、驱动程序等不能使用用户态同步机制的场景。它主要用于协调内核线程对共享资源的访问,支持阻塞等待,可用于实现类似互斥锁的功能。内核提供的接口包括 sema_init() 初始化信号量、down() 获取资源(P 操作)、up() 释放资源(V 操作)。与 spinlock 相比,信号量适用于允许睡眠的临界区,避免忙等,提高内核效率。


POSIX信号量(POSIX Semaphore)

​ POSIX 信号量是 POSIX 标准(IEEE 1003.1b)定义的用户空间多线程/多进程同步机制,支持命名信号量(跨进程)和无名信号量(同进程多线程)。其接口包括 sem_init() 初始化、sem_wait() 阻塞等待、sem_post() 释放资源、sem_destroy() 释放信号量结构等。POSIX 信号量可以基于 futex 等内核机制实现,兼顾效率和通用性,是现代类 Unix 系统中广泛使用的同步方式之一,适用于线程和进程间资源控制与互斥。

作者

kosa-as

发布于

2025-06-30

更新于

2025-07-12

许可协议

评论