目录

Linux内核同步笔记

Linux内核同步实现

  • preempt_enable
  • preempt_disable

c

#define preempt_disable() \
do { \
	preempt_count_inc(); \
	barrier(); \
} while (0)

c

#define preempt_enable() \
do { \
	barrier(); \
	if (unlikely(preempt_count_dec_and_test())) \
		__preempt_schedule(); \
} while (0)

// 继续分析
#define preempt_count_dec_and_test() \
	({ preempt_count_sub(1); should_resched(0); })

static __always_inline bool should_resched(int preempt_offset)
{
	return unlikely(raw_cpu_read_4(pcpu_hot.preempt_count) == preempt_offset);
}

可以看到preempt_count()获取计数

c

static __always_inline int preempt_count(void)
{
	return raw_cpu_read_4(pcpu_hot.preempt_count) & ~PREEMPT_NEED_RESCHED;
}
  1. barrier() 防止指令乱序
  2. preempt_count_inc 增加计数
  3. preempt_count_dec_and_test 减少计数
  4. 获取抢占计数

增加/减少计数

c

static __always_inline void __preempt_count_add(int val)
{
	raw_cpu_add_4(pcpu_hot.preempt_count, val);
}

static __always_inline void __preempt_count_sub(int val)
{
	raw_cpu_add_4(pcpu_hot.preempt_count, -val);
}

为什么这个变量可以实现抢占呢? 需要检查schedule实现分析

在Linux内核中,preempt_disable()preempt_enable() 是用于控制内核抢占(Preemption)的关键函数。 它们通过管理抢占计数器(preempt_count)来确保临界区代码的原子性,避免任务在内核态执行期间被其他高优先级任务抢占。以下是详细分析:

  1. 访问每CPU变量(Per-CPU Data)

    • 每CPU变量是每个CPU独有的数据,禁用抢占可防止任务被迁移到其他CPU。
    • 例如:

      c

      int *ptr = this_cpu_ptr(&my_percpu_var);
      preempt_disable();
      *ptr += 1;            /* 操作每CPU变量 */
      preempt_enable();
  2. 保护短临界区

    • 当需要保证一段代码不被其他任务打断,但无需处理中断或软中断时。
    • 例如:

      c

      preempt_disable();
      modify_global_non_sleepable_data();  /* 修改非睡眠安全的数据 */
      preempt_enable();
  3. 与自旋锁配合使用

    • 自旋锁(如spin_lock())内部会自动禁用抢占,但在某些手动管理场景中可能需要显式调用。
    • 例如,持有锁期间访问共享资源:

      c

      spin_lock(&lock);
      preempt_disable();
      critical_section();
      preempt_enable();
      spin_unlock(&lock);
机制抢占控制中断控制适用场景
preempt_disable()禁用任务调度不影响中断短临界区、每CPU变量
spin_lock()隐式禁用抢占可能禁用中断/软中断共享资源在多核/中断上下文访问

  • preempt_disable()preempt_enable() 必须严格配对。
  • 每次preempt_disable()增加计数器,preempt_enable()减少计数器。
  • 错误示例:

    c

    preempt_disable();
    preempt_disable();  /* 嵌套禁用抢占 */
    critical_section();
    preempt_enable();   /* 仅减少一次计数器,抢占仍被禁用! */
  • 若在preempt_disable()后调用可能睡眠的函数(如kmalloc(GFP_KERNEL)),会导致死锁或内核崩溃。
  • 原因:调度器无法切换任务,当前任务无法释放CPU。
  • 单核系统(非抢占内核)preempt_disable()可能无实际效果(因内核不可抢占),但仍需使用以保证代码可移植性。
  • 多核系统:禁用抢占仅对当前CPU有效,其他CPU仍可并发执行。
  • 不禁止中断:中断仍可发生,但中断处理程序执行完毕后,调度器不会抢占当前任务(因preempt_count > 0)。
  • 若需同时禁用中断,需配合local_irq_disable()

    c

    local_irq_disable();
    preempt_disable();
    /* 临界区:不受中断和任务抢占影响 */
    preempt_enable();
    local_irq_enable();

  • 优点:轻量级,仅操作计数器,适合高频短临界区。
  • 缺点
    • 长时间禁用抢占会导致调度延迟,影响系统实时性。
    • 不适用于需要睡眠的操作(如I/O等待)。

c

#include <linux/preempt.h>

void example_function(void) {
    preempt_disable();  // 禁用抢占

    // 临界区代码:不会被其他任务抢占
    access_shared_data();

    preempt_enable();   // 启用抢占,可能触发调度
}

关键点说明
作用禁用/启用内核抢占,保护短临界区代码的原子性。
实现机制通过preempt_count计数器控制抢占。
适用场景每CPU变量、非睡眠安全的短临界区、与锁配合使用。
注意事项严格嵌套配对、禁止睡眠、区分单核/多核行为、不保护中断。
替代方案自旋锁(处理多核并发+中断)、RCU(读多写少场景)。

合理使用preempt_disable()/preempt_enable()可在不引入锁开销的情况下,高效实现内核数据保护,但需严格遵守使用规则以避免系统稳定性问题。

https://elixir.bootlin.com/linux/v4.0/source/include/linux/types.h#L175

c

typedef struct {
	int counter;
} atomic_t;

typedef struct {
	long long counter;
} atomic64_t;
类别函数原型功能
初始化ATOMIC_INIT(int val)静态初始化原子变量为val
atomic_set(atomic_t *v, int i)动态设置原子变量值为i
基础操作atomic_read(const atomic_t *v)读取原子变量的值。
atomic_add(int i, atomic_t *v)原子地将v的值增加i
atomic_sub(int i, atomic_t *v)原子地将v的值减少i
atomic_inc(atomic_t *v)原子地递增v(等价于v += 1)。
atomic_dec(atomic_t *v)原子地递减v(等价于v -= 1)。
条件操作atomic_inc_and_test(atomic_t *v)递增v,若结果为0返回true
atomic_dec_and_test(atomic_t *v)递减v,若结果为0返回true
atomic_sub_and_test(int i, atomic_t *v)减去i,若结果为0返回true
返回值操作atomic_add_return(int i, atomic_t *v)增加i并返回新值。
atomic_sub_return(int i, atomic_t *v)减少i并返回新值。
函数原型功能
set_bit(int nr, volatile unsigned long *addr)原子地设置地址addr的第nr位为1。
clear_bit(int nr, volatile unsigned long *addr)原子地清除地址addr的第nr位为0。
test_and_set_bit(int nr, volatile unsigned long *addr)设置第nr位为1,返回旧值。
test_and_clear_bit(int nr, volatile unsigned long *addr)清除第nr位为0,返回旧值。

通用的模式

c

static inline void set_bit(int nr, volatile unsigned long *addr)
{
	unsigned long mask = BIT_MASK(nr);
	unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
	unsigned long flags;

	_atomic_spin_lock_irqsave(p, flags);
	*p  |= mask;
	_atomic_spin_unlock_irqrestore(p, flags);
}

static inline void clear_bit(int nr, volatile unsigned long *addr)
{
	unsigned long mask = BIT_MASK(nr);
	unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
	unsigned long flags;

	_atomic_spin_lock_irqsave(p, flags);
	*p &= ~mask;
	_atomic_spin_unlock_irqrestore(p, flags);
}

如果使用高效率的方式,使用汇编

https://elixir.bootlin.com/linux/v4.0/source/arch/arm/include/asm/atomic.h#L30

c

#define atomic_read(v)	ACCESS_ONCE((v)->counter)
#define atomic_set(v,i)	(((v)->counter) = (i))

参考链接

c

#define __ACCESS_ONCE(x) ({ \
	 __maybe_unused typeof(x) __var = (__force typeof(x)) 0; \
	(volatile typeof(x) *)&(x); })
#define ACCESS_ONCE(x) (*__ACCESS_ONCE(x))

在Linux内核中,自旋锁(spinlock)是一种用于多处理器(SMP)环境的同步机制,确保共享资源的互斥访问。以下是不同自旋锁变种的详细解释及使用场景:

  • spin_lock/spin_unlock
  • spin_lock_bh/spin_unlock_bh
  • spin_lock_irq/spin_unlock_irq
  • spin_lock_irqsave/spin_unlock_irqrestore
  • queued_spin_lock/queued_spin_unlock
  • 功能:最基本的自旋锁操作。
  • 行为
    • spin_lock():尝试获取锁,若锁被占用,则自旋等待。
    • spin_unlock():释放锁。
  • 适用场景
    • 非中断上下文(如线程、进程上下文)。
    • 共享资源不会被中断处理程序或软中断(Bottom Half)访问
  • 注意:若在中断上下文中可能访问同一锁,需使用其他变种(如spin_lock_irq())。

  • 功能:在获取锁的同时禁用软中断(Bottom Half)。
  • 行为
    • spin_lock_bh():禁用本地CPU的软中断,然后获取锁。
    • spin_unlock_bh():释放锁,并重新启用软中断。
  • 适用场景
    • 保护共享资源在**进程上下文与软中断(如tasklet、定时器)**之间的并发访问。
    • 例如:进程上下文与网络协议栈的软中断共享数据时。

  • 功能:在获取锁的同时禁用硬中断。
  • 行为
    • spin_lock_irq():禁用本地CPU的硬中断,然后获取锁。
    • spin_unlock_irq():释放锁,并重新启用硬中断。
  • 适用场景
    • 保护共享资源在进程上下文与硬中断处理程序之间的并发访问。
    • 注意:若在调用前中断已禁用,解锁后可能错误启用中断。此时应使用spin_lock_irqsave()

  • 功能:安全处理中断状态的锁操作。
  • 行为
    • spin_lock_irqsave():保存当前中断状态到变量,禁用本地CPU中断,然后获取锁。
    • spin_unlock_irqrestore():释放锁,并根据保存的状态恢复中断。
  • 适用场景
    • 共享资源在不确定中断是否已禁用的上下文中使用(如可重入函数)。
    • 确保中断状态的正确保存与恢复,避免破坏原有状态。

  • 功能:排队自旋锁的实现,解决传统自旋锁的高竞争问题。
  • 行为
    • 通过队列机制让等待锁的CPU按顺序获取,减少缓存行争用。
    • 提升多核系统下的扩展性和公平性。
  • 内核使用
    • 从Linux 3.10开始,x86架构默认使用排队自旋锁。
    • 用户通过通用接口(如spin_lock())调用,无需直接操作queued_spin_*
  • 优点:在高竞争场景下性能更优,避免“惊群效应”。

锁类型禁用中断类型适用场景
spin_lock()仅进程上下文,无中断/软中断竞争
spin_lock_bh()软中断(Bottom Half)进程上下文与软中断共享资源
spin_lock_irq()硬中断进程上下文与硬中断共享资源,且已知中断状态
spin_lock_irqsave()硬中断(带状态保存)进程上下文与硬中断共享资源,且需安全处理中断状态
queued_spin_lock()无(内核内部优化)高竞争场景,由内核自动选择

  1. 禁止在持有自旋锁时睡眠:可能导致死锁或内核崩溃。
  2. 锁持有时间应极短:自旋锁通过忙等待消耗CPU,长时间持有会降低性能。
  3. 区分单核与多核行为:在单核非抢占内核中,自旋锁可能退化为仅禁用抢占。

通过合理选择自旋锁变种,可确保内核数据的安全访问,同时兼顾性能与正确性。

https://elixir.bootlin.com/linux/v4.0/source/include/linux/spinlock_types.h#L32

c

typedef struct raw_spinlock {
	arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
	unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
	unsigned int magic, owner_cpu;
	void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map dep_map;
#endif
} raw_spinlock_t;

进入arch的实现

https://elixir.bootlin.com/linux/v4.0/source/arch/arm/include/asm/spinlock_types.h#L23

c

typedef struct {
	union {
		u32 slock;
		struct __raw_tickets {
#ifdef __ARMEB__
			u16 next;
			u16 owner;
#else
			u16 owner;
			u16 next;
#endif
		} tickets;
	};
} arch_spinlock_t;

https://elixir.bootlin.com/linux/v4.0/source/include/linux/spinlock.h#L188

c

static inline void spin_lock(spinlock_t *lock)
{
	raw_spin_lock(&lock->rlock);
}

#define raw_spin_lock(lock)	_raw_spin_lock(lock)

static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
	preempt_disable(); // 核心实现
	spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); // lockdep检测
	LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}

c

static __always_inline void spin_lock_irq(spinlock_t *lock)
{
	raw_spin_lock_irq(&lock->rlock);
}

函数调用路线:

graph LR
spin_lock_bh
	==> __raw_spin_lock_bh
		==> __local_bh_disable_ip
			==> preempt_count_add

最终,执行到了pcpu_hot.preempt_count计数功能实现

c

static inline void spin_lock_bh(spinlock_t *lock)
{
	raw_spin_lock_bh(&lock->rlock);
}

static inline void __raw_spin_lock_bh(raw_spinlock_t *lock)
{
	__local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET);
	spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
	LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}

static __always_inline void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
{
	preempt_count_add(cnt);
	barrier();
}

static __always_inline void __preempt_count_add(int val)
{
	raw_cpu_add_4(pcpu_hot.preempt_count, val);
}

c

static inline int spin_trylock(spinlock_t *lock)
{
	return raw_spin_trylock(&lock->rlock);
}

c

static inline void spin_unlock(spinlock_t *lock)
{
	raw_spin_unlock(&lock->rlock);
}

c

static inline void spin_unlock_bh(spinlock_t *lock)
{
	raw_spin_unlock_bh(&lock->rlock);
}

c

static inline void spin_unlock_irq(spinlock_t *lock)
{
	raw_spin_unlock_irq(&lock->rlock);
}

进入qspin实现

qspin锁数据

c

typedef struct qspinlock {
	union {
		atomic_t val;

		/*
		 * By using the whole 2nd least significant byte for the
		 * pending bit, we can allow better optimization of the lock
		 * acquisition for the pending bit holder.
		 */
#ifdef __LITTLE_ENDIAN
		struct {
			u8	locked;
			u8	pending;
		};
		struct {
			u16	locked_pending;
			u16	tail;
		};
#else
		struct {
			u16	tail;
			u16	locked_pending;
		};
		struct {
			u8	reserved[2];
			u8	pending;
			u8	locked;
		};
#endif
	};
} arch_spinlock_t;
  1. 如果lock可以直接获取,直接返回;
  2. 进入slowpath

c

static __always_inline void queued_spin_lock(struct qspinlock *lock)
{
	int val = 0;

	if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL)))
		return;

	queued_spin_lock_slowpath(lock, val);
}

slowpath实现

c

void queued_spin_lock_slowpath(struct qspinlock *lock)
{
	/*
	 * This looks funny, but it induces the compiler to inline both
	 * sides of the branch rather than share code as when the condition
	 * is passed as the paravirt argument to the functions.
	 */
	if (IS_ENABLED(CONFIG_PARAVIRT_SPINLOCKS) && is_shared_processor()) {
		if (try_to_steal_lock(lock, true)) {
			spec_barrier();
			return;
		}
		queued_spin_lock_mcs_queue(lock, true);
	} else {
		if (try_to_steal_lock(lock, false)) {
			spec_barrier();
			return;
		}
		queued_spin_lock_mcs_queue(lock, false);
	}
}

c

static __always_inline void queued_spin_unlock(struct qspinlock *lock)
{
	/*
	 * unlock() needs release semantics:
	 */
	smp_store_release(&lock->locked, 0);
}

// 直接进行smp释放
#define smp_store_release(p, v) do { kcsan_release(); __smp_store_release(p, v); } while (0)

// 对locked直接释放
do {									\
	compiletime_assert_atomic_type(*p);				\
	__smp_mb();							\
	WRITE_ONCE(*p, v);						\
} while (0)

https://elixir.bootlin.com/linux/v4.0/source/include/linux/mutex.h#L50

c

struct mutex {
	/* 1: unlocked, 0: locked, negative: locked, possible waiters */
	atomic_t		count;
	spinlock_t		wait_lock;
	struct list_head	wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
	struct task_struct	*owner;
#endif
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
	struct optimistic_spin_queue osq; /* Spinner MCS lock */
#endif
#ifdef CONFIG_DEBUG_MUTEXES
	void			*magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
};

如何判断mutex加锁

c

/**
 * mutex_is_locked - is the mutex locked
 * @lock: the mutex to be queried
 *
 * Returns 1 if the mutex is locked, 0 if unlocked.
 */
static inline int mutex_is_locked(struct mutex *lock)
{
	return atomic_read(&lock->count) != 1;
}

c

void __sched mutex_lock(struct mutex *lock)
{
	might_sleep();
	/*
	 * The locking fastpath is the 1->0 transition from
	 * 'unlocked' into 'locked' state.
	 */
	__mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath);
	mutex_set_owner(lock);
}

/* 直接设置owner */
static inline void mutex_set_owner(struct mutex *lock)
{
	lock->owner = current;
}

下面的函数分析:

  1. 如果可以直接设置原子变量
  2. 进入慢速的实现

c

static inline void
__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
{
	if (unlikely(atomic_xchg(count, 0) != 1))
		if (likely(atomic_xchg(count, -1) != 1))
			fail_fn(count)
}

分析慢速实现

c

__visible void __sched
__mutex_lock_slowpath(atomic_t *lock_count)
{
	struct mutex *lock = container_of(lock_count, struct mutex, count);

	__mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0,
			    NULL, _RET_IP_, NULL, 0);
}

https://elixir.bootlin.com/linux/v4.0/source/kernel/locking/mutex.c#L517

https://elixir.bootlin.com/linux/v4.0/source/kernel/locking/mutex.c#L421

c

void __sched mutex_unlock(struct mutex *lock)
{
#ifndef CONFIG_DEBUG_MUTEXES
	mutex_clear_owner(lock);
#endif
	__mutex_fastpath_unlock(&lock->count, __mutex_unlock_slowpath);
}

进入快速释放函数

c

static inline void
__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
{
	if (unlikely(atomic_xchg(count, 1) != 0))
		fail_fn(count);
}

进入慢速函数实现

https://elixir.bootlin.com/linux/v4.0/source/kernel/locking/mutex.c#L724

c

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

#define __SEMAPHORE_INITIALIZER(name, n)				\
{									\
	.lock		= __RAW_SPIN_LOCK_UNLOCKED((name).lock),	\
	.count		= n,						\
	.wait_list	= LIST_HEAD_INIT((name).wait_list),		\
}

c

void down(struct semaphore *sem)
{
	unsigned long flags;

	raw_spin_lock_irqsave(&sem->lock, flags);
	if (likely(sem->count > 0))
		sem->count--;
	else
		__down(sem);
	raw_spin_unlock_irqrestore(&sem->lock, flags);
}

进入内核实现 https://elixir.bootlin.com/linux/v4.0/source/kernel/locking/semaphore.c#L204

c

void up(struct semaphore *sem)
{
	unsigned long flags;

	raw_spin_lock_irqsave(&sem->lock, flags);
	if (likely(list_empty(&sem->wait_list)))
		sem->count++;
	else
		__up(sem);
	raw_spin_unlock_irqrestore(&sem->lock, flags);
}

进入up的具体实现

c

static noinline void __sched __up(struct semaphore *sem)
{
	struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
						struct semaphore_waiter, list);
	list_del(&waiter->list);
	waiter->up = true;
	wake_up_process(waiter->task);
}

Linux内核中的读写锁(Reader-Writer Locks)主要用于优化对共享资源的高并发访问,尤其是在读多写少的场景下。其设计目标是在保证数据一致性的前提下,最大化读取操作的并行性,同时确保写入操作的独占性。以下是Linux内核中两种主要读写锁的实现分析:


适用场景:不可休眠的上下文(如中断处理程序、原子上下文),基于自旋锁实现。

c

// include/linux/rwlock_types.h
typedef struct {
    arch_rwlock_t raw_lock;  // 架构相关的底层锁表示
} rwlock_t;
  • 底层实现:依赖于架构(如x86使用原子计数和状态标志),通常通过原子指令或自旋锁机制实现。
  • 读者加锁

    c

    read_lock(rwlock_t *lock);
    • 原子操作增加读者计数,若当前无写者持有锁则立即获得访问权;否则自旋等待。
  • 写者加锁

    c

    write_lock(rwlock_t *lock);
    • 等待所有读者释放锁后,独占获取锁,期间通过自旋避免上下文切换。
  • 解锁

    c

    read_unlock(rwlock_t *lock);
    write_unlock(rwlock_t *lock);
    • 减少读者计数或释放写锁,唤醒等待的读者/写者。
  • 无休眠:读者和写者均通过自旋等待,适用于非抢占式上下文。
  • 低开销:无上下文切换成本,适合极短临界区。
  • 潜在问题:长时间持有锁会导致CPU资源浪费,可能引发写者饥饿(若读者持续进入)。

适用场景:可休眠的进程上下文,基于信号量实现,支持复杂的同步策略。

c

// include/linux/rwsem.h
struct rw_semaphore {
    atomic_long_t count;      // 组合计数器:高32位为等待计数,低32位为活跃读者/写者标志
    struct list_head wait_list;  // 等待队列(读者或写者)
    // 其他调试和优化字段...
};
  • 计数器设计
    • 写者标记:若最低位(0x1)为1,表示写者持有锁。
    • 读者计数:低32位中除最低位的其余位表示活跃读者数量。
    • 等待计数:高32位记录等待的写者或读者数量。
  • 读者加锁

    c

    down_read(struct rw_semaphore *sem);
    • 若当前无写者持有锁且无写者等待,立即增加读者计数;否则加入等待队列并休眠。
  • 写者加锁

    c

    down_write(struct rw_semaphore *sem);
    • 等待所有活跃读者/写者释放锁后独占访问,期间休眠让出CPU。
  • 解锁

    c

    up_read(struct rw_semaphore *sem);
    up_write(struct rw_semaphore *sem);
    • 减少读者计数或清除写者标记,唤醒队列中的等待者(优先唤醒写者以避免饥饿)。
  • 公平性策略
    • 写者优先:当写者等待时,新读者会被阻塞,防止写者饥饿。
    • 等待队列:通过FIFO或优先级调度管理等待者,平衡读者与写者的公平性。
  • 休眠支持:在无法立即获取锁时,任务进入休眠状态,避免CPU空转。
  • 优化机制
    • 无竞争快速路径:无竞争时直接通过原子操作完成加锁/解锁,无需操作等待队列。
    • 乐观自旋:在某些实现中,写者可能会短暂自旋以尝试快速获取锁,减少休眠开销。

特性rwlock_trw_semaphore
上下文仅限非休眠上下文(中断、原子操作)进程上下文(可休眠)
等待机制自旋等待(忙等待)休眠等待
公平性无明确策略,可能写者饥饿写者优先,减少饥饿风险
开销低(无上下文切换)较高(休眠唤醒、队列管理)
适用场景极短临界区、高频读操作长临界区、需公平性保障

  • 原子操作与内存屏障

    • rwlock_t使用原子指令(如atomic_add)管理读者计数,结合内存屏障(barrier())确保操作顺序。
    • rw_semaphore通过atomic_long_t的原子操作更新组合计数器,配合显式屏障(如smp_mb__before_atomic)保证多核一致性。
  • 调试与检测

    • 锁依赖检测:内核配置CONFIG_DEBUG_LOCK_ALLOC时,会跟踪锁的获取顺序以防止死锁。
    • 死锁预警:通过CONFIG_DEBUG_ATOMIC_SLEEP检查在原子上下文中误用rw_semaphore

场景1:网络包统计(高频读,低频写)

  • 使用rwlock_t保护全局统计计数器:

    c

    rwlock_t stats_lock;
    unsigned int packet_count;
    
    // 读者(中断上下文)
    read_lock(&stats_lock);
    unsigned int count = packet_count;
    read_unlock(&stats_lock);
    
    // 写者(定时器上下文)
    write_lock(&stats_lock);
    packet_count = 0;
    write_unlock(&stats_lock);

场景2:文件系统元数据更新(需公平性)

  • 使用rw_semaphore保护目录结构:

    c

    struct rw_semaphore dir_sem;
    
    // 读者(遍历目录)
    down_read(&dir_sem);
    // 读取目录项
    up_read(&dir_sem);
    
    // 写者(创建文件)
    down_write(&dir_sem);
    // 修改目录结构
    up_write(&dir_sem);

相关内容