内核同步
一、为什么需要同步
- 多核:CPU 并发访问共享数据
- 内核抢占:调度器随时可能切走
- 中断:随时打断当前代码
二、原子操作
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_t 或 seq_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 |