内核同步

一、为什么需要同步

二、原子操作

1. 整数原子

atomic_t v = ATOMIC_INIT(0);
atomic_inc(&v);
atomic_dec(&v);
atomic_add(10, &v);
atomic_sub(&v);
atomic_read(&v);
atomic_set(&v, 100);
int old = atomic_fetch_add(1, &v);
atomic_inc_and_test(&v);           // == 1?
atomic_dec_and_test(&v);           // == 0?
atomic_cmpxchg(&v, old, new);

2. 位原子

set_bit(nr, addr);
clear_bit(nr, addr);
change_bit(nr, addr);
test_bit(nr, addr);
test_and_set_bit(nr, addr);
test_and_clear_bit(nr, addr);

3. 64位原子(atomic64_t)

atomic64_t v;
atomic64_inc(&v);
// ...

4. C11 风格

atomic_t v;                   // 或 ATOMIC_INIT
atomic_set(&v, 1);
atomic_inc(&v);
atomic_fetch_add(1, &v);

三、自旋锁(Spinlock)

1. 基本

DEFINE_SPINLOCK(my_lock);
spin_lock(&my_lock);
// 临界区(必须短!)
spin_unlock(&my_lock);

2. API

spin_lock(&lock);                    // 加锁
spin_lock_irqsave(&lock, flags);     // 加锁 + 关本地中断
spin_lock_irq(&lock);                // 加锁 + 关中断(已知状态)
spin_lock_bh(&lock);                 // 加锁 + 关软中断

spin_unlock(&lock);
spin_unlock_irqrestore(&lock, flags);
spin_unlock_irq(&lock);
spin_unlock_bh(&lock);

spin_trylock(&lock);                 // 不阻塞
spin_is_locked(&lock);

3. 规则

4. 调试

echo 1 > /proc/sys/kernel/lock_stat
cat /proc/lock_stat

四、互斥锁(Mutex)

DEFINE_MUTEX(my_mutex);
mutex_lock(&my_mutex);
// 可睡眠、可调度
mutex_unlock(&my_mutex);

mutex_trylock(&my_mutex);
mutex_is_locked(&my_mutex);

特性:
- 进程上下文使用
- 可睡眠(内部用 futex)
- 自动启用优先级继承(PI):避免优先级反转
- 不能用于中断上下文

五、读写锁

DEFINE_RWLOCK(my_rw);
read_lock(&my_rw);
// 读
read_unlock(&my_rw);

write_lock(&my_rw);
// 写
write_unlock(&my_rw);

推荐 rwlock_tseq_file 模式。

六、seqlock(顺序锁)

DEFINE_SEQLOCK(my_seq);

unsigned int seq;
seq = read_seqbegin(&my_seq);
// 读(如果冲突就重读)
read_seqretry(&my_seq, seq);

write_seqlock(&my_seq);
// 写(不能睡眠)
write_sequnlock(&my_seq);

特点:
- 写优先
- 读快、写慢
- 适合读多写少
- jiffies、time 等用它

七、RCU(Read-Copy-Update)

struct my_data {
    int value;
    struct rcu_head rcu;
};

static struct my_data *g_data;

void reader(void) {
    struct my_data *p;
    rcu_read_lock();
    p = rcu_dereference(g_data);
    printk("%d\n", p->value);
    rcu_read_unlock();
}

void writer(struct my_data *new) {
    struct my_data *old = g_data;
    rcu_assign_pointer(g_data, new);
    synchronize_rcu();        // 等待所有读者
    kfree(old);
}

// 异步版本
void writer_async(struct my_data *new) {
    struct my_data *old = g_data;
    rcu_assign_pointer(g_data, new);
    call_rcu(&old->rcu, my_free);   // 延迟释放
}

特点:
- 读者极快(无锁)
- 写者慢、要等所有读者
- 适合读多写少、读路径关键
- 内核 2.5+ 大量使用

八、per-CPU 变量

DEFINE_PER_CPU(int, my_counter);

void inc_my_counter(void) {
    int *cnt = this_cpu_ptr(&my_counter);
    (*cnt)++;
}

s64 sum = 0;
for_each_possible_cpu(cpu)
    sum += per_cpu(my_counter, cpu);

九、内存屏障

mb();        // 全屏障
rmb();       // 读屏障
wmb();       // 写屏障

smp_mb();    // SMP 屏障(单 CPU 时为空)
smp_rmb();
smp_wmb();

barrier();   // 编译器屏障(不阻止 CPU)

smp_store_release / smp_load_acquire(RCU 风格):

smp_store_release(&ptr, new);
smp_load_acquire(&ptr);

十、锁的选择

场景 推荐
ISR 中 spinlock + 关中断
短临界区 spinlock
长临界区 mutex
读多写少 rwlock / RCU
延迟敏感读 seqlock
简单计数器 atomic_t
per-cpu 数据 per_cpu