Linux 内核同步管理全解:原理 + 实战 + 考点


🔥 推荐:《Yocto项目实战教程:高效定制嵌入式Linux系统》

京东正版促销,欢迎支持原创!

链接:https://item.jd.com/15020438.html




一、为什么需要同步机制?

Linux 是一个支持 多核并发 + 抢占式调度 的操作系统,多个内核线程可能同时访问同一份内存区域,例如:驱动共享某个全局变量、文件系统缓存读写、网络缓冲区处理等等。

如果没有同步机制,就会产生竞态条件(Race Condition),导致数据错乱、内核崩溃甚至安全漏洞。

因此,Linux 内核实现了一整套高效的同步机制,用来协调不同 CPU、不同执行上下文之间对共享资源的访问。


二、Linux 中的主要同步机制

同步机制 是否可睡眠 适用场景 常见用法 内核位置
原子变量 简单计数/标志 状态标志、自增自减 include/linux/atomic.h
自旋锁 中断处理、短临界区 硬件寄存器、短时间保护 include/linux/spinlock.h
信号量 多资源管理 限制并发访问数 include/linux/semaphore.h
互斥锁 临界区保护 普通数据结构保护 include/linux/mutex.h
RCU 部分读者无锁 读多写少的表结构 任务列表、网络表项 kernel/rcu/

三、五种同步机制逐一讲解

3.1 原子变量

✅ 场景:状态标志、自增计数器
c 复制代码
atomic_t count;
atomic_set(&count, 0);

atomic_inc(&count);  // ++count
atomic_dec(&count);  // --count

if (atomic_read(&count) == 0) {
    // 说明资源清空或条件满足
}
  • 原子变量适合简短逻辑,如计数器、标志位。
  • 不适合保护复杂数据结构。
✅ 面试考点:
  • 原子变量是否需要锁?→ 否。
  • 原子操作能否用于中断上下文?→ 可以。

3.2 自旋锁(Spinlock)

✅ 场景:短时间保护、中断上下文
c 复制代码
spinlock_t my_lock;
spin_lock_init(&my_lock);

spin_lock(&my_lock);
/* 访问共享资源 */
spin_unlock(&my_lock);

🔥 推荐:《Yocto项目实战教程:高效定制嵌入式Linux系统》

京东正版促销,欢迎支持原创!

链接:https://item.jd.com/15020438.html



✅ 中断安全版本:
c 复制代码
unsigned long flags;
spin_lock_irqsave(&my_lock, flags);
/* 临界区 */
spin_unlock_irqrestore(&my_lock, flags);
✅ 特点:
  • 获取不到锁会 原地忙等(自旋),适合快速操作;
  • 不可用于睡眠上下文,否则死锁;
  • 常用于中断处理函数、底半部等高优先级逻辑。
✅ 面试考点:
  • spin_lock 与 mutex 有什么区别?
  • 中断上下文是否可以使用 mutex?→ 否。

3.3 信号量(Semaphore)

✅ 场景:控制多个线程对有限资源访问
c 复制代码
struct semaphore sem;
sema_init(&sem, 3);  // 最多允许3个并发

/* 请求资源 */
down(&sem);  // 若资源不足,将睡眠

/* 使用资源 */

/* 释放资源 */
up(&sem);
  • 适合表示资源池,如"3台打印机"、"10个缓存槽位";
  • 已逐渐被 mutex 替代。
✅ 面试考点:
  • down()/up() 会阻塞线程吗?→ 是。
  • 信号量适合中断中使用吗?→ 否(会睡眠)。

3.4 互斥锁(Mutex)

✅ 场景:线程间对共享数据的互斥访问
c 复制代码
struct mutex my_mutex;
mutex_init(&my_mutex);

mutex_lock(&my_mutex);
/* 临界区代码 */
mutex_unlock(&my_mutex);
  • 获取不到锁会让当前线程 挂起等待,资源释放后再调度回来;
  • 不适合中断处理。
✅ 特点对比:
比较项 spinlock mutex
可否睡眠
场景 中断/底半部 普通线程
获取失败 自旋等待 睡眠等待
✅ 面试考点:
  • mutex 和 semaphore 区别?→ semaphore 是计数,mutex 仅一人。

3.5 RCU(Read-Copy-Update)

✅ 场景:读多写少,如任务列表、网络表项
读操作(不加锁):
c 复制代码
rcu_read_lock();
my_ptr = rcu_dereference(global_ptr);
/* 安全读取数据 */
rcu_read_unlock();
写操作(复制更新):
c 复制代码
new_ptr = kmalloc(...);
/* 修改副本 */
rcu_assign_pointer(global_ptr, new_ptr);
synchronize_rcu();
/* 释放旧数据 */
kfree(old_ptr);
✅ 特点:
  • 读性能极高,不加锁;
  • 写复杂,需注意数据生命周期;
  • 常用于链表、哈希表等结构。
✅ 面试考点:
  • RCU 是否支持多读多写?→ 多读 + 单写 + 延迟销毁。
  • RCU 与普通锁最大区别?→ 读时不阻塞。

四、实战示例:共享计数器保护

❌ 错误示例:未加锁

c 复制代码
static int count = 0;

void do_work(void) {
    count++;  // 多线程访问存在竞态!
}

✅ 正确方式:使用原子变量

c 复制代码
static atomic_t count = ATOMIC_INIT(0);

void do_work(void) {
    atomic_inc(&count);
}

✅ 或使用自旋锁

c 复制代码
static int count = 0;
static spinlock_t lock;

void do_work(void) {
    spin_lock(&lock);
    count++;
    spin_unlock(&lock);
}

✅ 或使用 mutex(若可睡眠)

c 复制代码
static int count = 0;
static struct mutex my_mutex;

void do_work(void) {
    mutex_lock(&my_mutex);
    count++;
    mutex_unlock(&my_mutex);
}

五、面试常见问题汇总(含答案)

  1. spinlock 和 mutex 有何区别?

    • spinlock 不可睡眠、适用于中断上下文;mutex 会睡眠、适合线程间同步。
  2. 原子变量需要加锁吗?

    • 不需要,已具备原子性。
  3. 哪些锁不能用于中断?

    • mutex、semaphore 会睡眠,不能用于中断处理。
  4. RCU 为什么性能高?

    • 读操作无锁,不阻塞任何线程,极高并发性。
  5. 如何避免死锁?

    • 保持锁获取顺序一致;禁止在持锁时调用睡眠函数(如 mutex + msleep());中断上下文避免调用可睡眠接口。

六、小结:如何选用同步机制?

场景 建议同步方式
标志位、计数器 原子变量
中断处理、底半部 自旋锁
用户进程临界区 互斥锁 mutex
限制访问数量(N个资源) 信号量 semaphore
读多写少表结构 RCU

七、结束语

Linux 内核的同步机制是驱动开发和内核编程中的"必修课",掌握好它不仅能写出正确的代码,更是迈入高级内核开发的关键一步。

📚 🔥 推荐:《Yocto项目实战教程:高效定制嵌入式Linux系统》

京东正版促销,欢迎支持原创!

链接:https://item.jd.com/15020438.html


🎥 视频教程请关注 B 站:"嵌入式 Jerry"

相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式