ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【Note】《Linux 内核深度解析:基于 ARM64 架构的 Linux 4.x 内核》 第五章:内核互斥技术

2026/8/18 14:44:05 拓冰建站 浏览量
【Note】《Linux 内核深度解析:基于 ARM64 架构的 Linux 4.x 内核》 第五章:内核互斥技术

《Linux 内核深度解析:基于 ARM64 架构的 Linux 4.x 内核》 第五章:内核互斥技术

在支持多核、抢占、异步中断的 Linux 内核中,如何正确、安全地访问共享资源,是稳定性与性能的核心问题。


5.1 信号量(Semaphore)

  • Linux 信号量是计数型锁,支持多个任务通过 down() / up() 机制协调资源访问。

  • 可睡眠等待,适合访问较慢设备或长时间资源持有的场景。

  • 核心结构体:

    struct semaphore {raw_spinlock_t lock;unsigned int count;struct list_head wait_list;
    };
    

5.2 读写信号量(RW Semaphore)

  • 支持多个读者同时访问资源,写者需独占。

  • 适合读多写少的共享数据,如 inode 属性。

  • API 示例:

    down_read();    // 读锁
    up_read();
    down_write();   // 写锁
    up_write();
    

5.3 互斥锁(Mutex)

  • 是轻量级睡眠锁,适用于线程间互斥。
  • 和信号量不同,mutex 只能由加锁者解锁。
  • ARM64 上 mutex_lock() 可进入睡眠,使用等待队列实现。

5.4 实时互斥锁(RT Mutex)

  • 专为 RT_PREEMPT 实时内核设计。
  • 可防止优先级反转(使用优先级继承 PI)。
  • 用于 futex、实时线程调度器中。

5.5 原子变量(atomic_t)

  • 对整数变量提供无锁原子操作,适合状态位、引用计数等。

  • ARM64 使用 LDXR/STXR 实现原子性。

  • 示例:

    atomic_inc(&val);
    if (atomic_dec_and_test(&val)) { ... }
    

5.6 自旋锁(Spinlock)

  • 在无法睡眠的上下文(如中断)中使用的基本锁机制。
  • spin_lock() 会忙等待,直到锁可用。
  • 适合临界区很短的情况;支持 irqsave

5.7 读写自旋锁(rwlock)

  • 多个读者可并发,写者独占。
  • 比普通自旋锁更高效,但结构复杂。
  • ARM64 实现通过 atomic64_t 控制锁状态。

5.8 顺序锁(seqlock)

5.8.1 完整版顺序锁

  • 写操作加锁,同时递增序列号;
  • 读操作可无锁,读取前后对比序号是否一致。

5.8.2 简化版顺序锁(seqcount)

  • 仅记录序列号,无实际锁;
  • 适合嵌入其他锁中,如 timekeeper 结构。

5.9 禁止内核抢占(preempt_disable)

  • preempt_disable() 阻止当前任务被抢占;
  • 不会屏蔽中断,仅用于保护短期临界区;
  • 与 spinlock 配合使用,常用于内核调度相关代码。

5.10 进程与软中断互斥

  • 软中断不会抢占进程上下文;
  • 通过 local_bh_disable() 屏蔽本地软中断;
  • 通常在 netif_rx() 等网络函数中使用。

5.11 进程与硬中断互斥

  • 使用 spin_lock_irqsave() 同时关闭本地中断并加锁;
  • 硬中断可能在任意时刻打断执行,必须保证一致性。

5.12 每处理器变量(per-CPU Variable)

5.12.1 静态 per-CPU

  • 使用 DEFINE_PER_CPU() 声明;
  • 编译时确定,效率高。

5.12.2 动态 per-CPU

  • 使用 alloc_percpu() 分配;
  • 适用于运行时可变数量对象。

5.12.3 访问 per-CPU

  • 通过 this_cpu_ptr() 获取当前 CPU 的对应变量;
  • 访问效率高,无需加锁。

5.13 每处理器计数器(per-CPU counter)

  • 用于高频递增/递减操作(如页缓存引用);
  • 结合全局总和与本地计数实现。
percpu_counter_add(&pc, 1);
percpu_counter_sum(&pc);

5.14 内存屏障(Memory Barrier)

5.14.1 编译器屏障(barrier())

  • 防止 GCC 等优化器重排指令。

5.14.2 处理器内存屏障

  • 使用 smp_mb(), smp_rmb(), smp_wmb()
  • 确保多核 CPU 中执行顺序一致。

5.14.3 MMIO 写屏障

  • 对设备寄存器写操作顺序控制;
  • ARM64 提供 wmb() 等。

5.14.4 隐含内存屏障

  • spin_unlock()mutex_unlock() 隐含内存屏障;
  • 一般不需要额外加 mb()

5.14.5 ARM64 处理器屏障

  • ARM64 支持:

    • dsb sy:系统范围屏障
    • dmb ish:内部共享屏障
    • isb:指令同步屏障

5.15 RCU(Read-Copy-Update)

5.15.1 使用方法

  • rcu_read_lock(), rcu_read_unlock() 保护读路径;
  • 更新路径复制数据结构并用 call_rcu() 延迟释放。

5.15.2 技术原理

  • 利用“宽限期”(Grace Period)确保所有旧读者退出后才清理;
  • 使用 quiescent state + callback 队列实现;
  • 性能极高,适用于高并发读。

5.16 可睡眠 RCU(SRCU)

5.16.1 使用方法

  • 与 RCU 相似,但读路径可睡眠;
  • 适合 block 层、文件系统等可能阻塞的上下文。

5.16.2 技术原理

  • 维护每 CPU 两个引用计数器;
  • 延迟 callback 直到所有读者退出两个 epoch。

5.17 死锁检测工具 lockdep

5.17.1 使用方法

  • 开启 CONFIG_LOCKDEP
  • 自动检测死锁链、锁顺序问题;
  • 适用于调试与内核开发。

5.17.2 技术原理

  • 为每个锁维护依赖图;
  • 记录加锁路径,通过图搜索检测环路。

总结

Linux 内核互斥机制种类繁多,从基础的信号量、自旋锁,到复杂的 RCU、顺序锁,再到每处理器变量与内存屏障,构建起了内核安全运行的完整“并发保障体系”。

分类示例机制特性说明
可睡眠锁mutex, semaphore用于线程上下文,可阻塞
忙等锁spinlock, rwlock不可睡眠,适用于中断、原子操作
无锁机制atomic_t, per-CPU高性能,适用于简化数据共享
高级同步RCU, seqlock读高并发、写复杂
工具支持lockdep自动死锁检测