|
|
@@ -1,12 +1,26 @@
|
|
|
---
|
|
|
title: 并发同步与原子操作
|
|
|
-tags: [Linux驱动, 并发, 锁, 原子操作, 嵌入式, 自旋锁, 信号量, 互斥体, 读写锁, 顺序锁, RCU]
|
|
|
+tags:
|
|
|
+ [
|
|
|
+ Linux驱动,
|
|
|
+ 并发,
|
|
|
+ 锁,
|
|
|
+ 原子操作,
|
|
|
+ 嵌入式,
|
|
|
+ 自旋锁,
|
|
|
+ 信号量,
|
|
|
+ 互斥体,
|
|
|
+ 读写锁,
|
|
|
+ 顺序锁,
|
|
|
+ RCU,
|
|
|
+ ]
|
|
|
created: 2026-09-16
|
|
|
updated: 2026-09-17
|
|
|
pdf_ref:
|
|
|
- "【正点原子】I.MX6U嵌入式Linux驱动开发指南V2.0.1 - 第四十七章 Linux并发与竞争"
|
|
|
- "【正点原子】I.MX6U嵌入式Linux驱动开发指南V2.0.1 - 第四十八章 Linux并发与竞争实验"
|
|
|
---
|
|
|
+
|
|
|
# 并发同步与原子操作
|
|
|
|
|
|
> 💡 **关联知识**: [[FreeRTOS学习笔记/07-中断管理与临界段]] | [[03-Linux驱动开发核心/05-中断下半部处理]] | [[03-Linux驱动开发核心/07-platform总线模型]]
|
|
|
@@ -42,8 +56,8 @@ sequenceDiagram
|
|
|
|
|
|
### 1.2 竞态条件产生场景
|
|
|
|
|
|
-| 场景 | 说明 | 危害程度 |
|
|
|
-| ----------------------- | ---------------------------------------------------- | ---------- |
|
|
|
+| 场景 | 说明 | 危害程度 |
|
|
|
+| ----------------- | ---------------------------------------------------- | ---------- |
|
|
|
| **中断与进程** | 进程访问共享数据时被中断,中断处理函数也访问同一数据 | ⭐⭐⭐⭐ |
|
|
|
| **多进程/多线程** | 多个进程或线程同时读写共享内存 | ⭐⭐⭐ |
|
|
|
| **多CPU(SMP)** | 多核处理器上多个CPU核心同时执行访问同一数据 | ⭐⭐⭐⭐⭐ |
|
|
|
@@ -94,8 +108,8 @@ Linux是多任务操作系统,以下因素导致并发不可避免:
|
|
|
|
|
|
Linux内核提供的同步机制从"轻"到"重"排列如下,选择哪一种是驱动开发中最基础的判断:
|
|
|
|
|
|
-| 机制 | 典型类型 | 是否可睡眠 | 中断上下文 | 适用场景 |
|
|
|
-| ------------------ | --------------------- | ---------- | ---------------------- | -------------------------- |
|
|
|
+| 机制 | 典型类型 | 是否可睡眠 | 中断上下文 | 适用场景 |
|
|
|
+| ------------ | ------------------- | ---------- | ---------------------- | -------------------------- |
|
|
|
| **原子操作** | `atomic_t` | 否 | 可用 | 单个整数计数、状态标志 |
|
|
|
| **自旋锁** | `spinlock_t` | 否 | 可用 | 极短临界区、中断上下文 |
|
|
|
| **读写锁** | `rwlock_t` | 否 | 可用 | 读多写少、临界区短 |
|
|
|
@@ -124,8 +138,8 @@ typedef struct {
|
|
|
|
|
|
### 2.2 原子变量操作API
|
|
|
|
|
|
-| 函数 | 说明 |
|
|
|
-| ----------------------------------------------- | ---------------------- |
|
|
|
+| 函数 | 说明 |
|
|
|
+| --------------------------------------------- | ---------------------- |
|
|
|
| `ATOMIC_INIT(int i)` | 定义原子变量时初始化值 |
|
|
|
| `int atomic_read(atomic_t *v)` | 读取原子变量的值 |
|
|
|
| `void atomic_set(atomic_t *v, int i)` | 设置原子变量的值 |
|
|
|
@@ -169,8 +183,8 @@ static int led_release(struct inode *inode, struct file *filp)
|
|
|
|
|
|
### 2.4 原子位操作API
|
|
|
|
|
|
-| 函数 | 说明 |
|
|
|
-| ------------------------------------------- | -------------------- |
|
|
|
+| 函数 | 说明 |
|
|
|
+| ----------------------------------------- | -------------------- |
|
|
|
| `void set_bit(int nr, void *p)` | 设置第nr位为1 |
|
|
|
| `void clear_bit(int nr, void *p)` | 清除第nr位 |
|
|
|
| `void change_bit(int nr, void *p)` | 反转第nr位 |
|
|
|
@@ -218,8 +232,8 @@ typedef struct spinlock {
|
|
|
|
|
|
### 3.3 自旋锁API
|
|
|
|
|
|
-| 函数 | 说明 | 使用场景 |
|
|
|
-| ----------------------------------------- | --------------------- | ------------ |
|
|
|
+| 函数 | 说明 | 使用场景 |
|
|
|
+| --------------------------------------- | --------------------- | ------------ |
|
|
|
| `DEFINE_SPINLOCK(spinlock_t lock)` | 静态定义并初始化 | 全局锁 |
|
|
|
| `void spin_lock_init(spinlock_t *lock)` | 动态初始化 | 运行时初始化 |
|
|
|
| `void spin_lock(spinlock_t *lock)` | 获取锁(忙等待) | 进程上下文 |
|
|
|
@@ -229,8 +243,8 @@ typedef struct spinlock {
|
|
|
|
|
|
### 3.4 中断安全的自旋锁API
|
|
|
|
|
|
-| 函数 | 说明 |
|
|
|
-| ---------------------------------------------------------------------- | ------------------------------ |
|
|
|
+| 函数 | 说明 |
|
|
|
+| -------------------------------------------------------------------- | ------------------------------ |
|
|
|
| `void spin_lock_irq(spinlock_t *lock)` | 禁止本地中断并获取锁 |
|
|
|
| `void spin_unlock_irq(spinlock_t *lock)` | 释放锁并开启本地中断 |
|
|
|
| `void spin_lock_irqsave(spinlock_t *lock, unsigned long flags)` | 保存中断状态、禁止中断、获取锁 |
|
|
|
@@ -342,13 +356,13 @@ static ssize_t led_write(struct file *filp, const char __user *buf,
|
|
|
|
|
|
### 4.4 自旋锁 vs 信号量对比表
|
|
|
|
|
|
-| 特性 | 自旋锁 | 信号量 |
|
|
|
-| -------------------- | ------------------ | ------------------- |
|
|
|
+| 特性 | 自旋锁 | 信号量 |
|
|
|
+| -------------- | ------------------ | ------------------- |
|
|
|
| **等待方式** | 忙等待(自旋) | 睡眠等待 |
|
|
|
| **CPU占用** | 占用CPU | 不占用CPU |
|
|
|
| **可睡眠** | ❌ 不可以 | ✅ 可以 |
|
|
|
| **中断上下文** | ✅ 可以使用 | ❌ 不可以 |
|
|
|
-| **临界区长度** | 短(μs级) | 长(ms级) |
|
|
|
+| **临界区长度** | 短(μs级) | 长(ms级) |
|
|
|
| **性能开销** | 低(忙等) | 高(上下文切换) |
|
|
|
| **适用场景** | 中断处理、短临界区 | 长时间操作、I/O操作 |
|
|
|
|
|
|
@@ -419,14 +433,14 @@ static ssize_t led_write(struct file *filp, const char __user *buf,
|
|
|
|
|
|
### 5.4 互斥体 vs 自旋锁对比
|
|
|
|
|
|
-| 特性 | 互斥体 | 自旋锁 |
|
|
|
-| -------------------- | ----------- | ----------- |
|
|
|
+| 特性 | 互斥体 | 自旋锁 |
|
|
|
+| -------------- | ----------- | ----------- |
|
|
|
| **睡眠** | ✅ 可以睡眠 | ❌ 不能睡眠 |
|
|
|
| **持有时长** | 可以较长 | 必须很短 |
|
|
|
| **中断上下文** | ❌ 不能使用 | ✅ 可以使用 |
|
|
|
| **优先级继承** | ✅ 支持 | ❌ 不支持 |
|
|
|
| **递归锁** | ❌ 不支持 | ❌ 不支持 |
|
|
|
-| **调试支持** | ✅ 丰富 | ⚠️ 有限 |
|
|
|
+| **调试支持** | ✅ 丰富 | ⚠️ 有限 |
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -504,8 +518,8 @@ DEFINE_RWLOCK(my_rwlock);
|
|
|
|
|
|
**读锁 API**(共享)——原书表 47.3.3.1:
|
|
|
|
|
|
-| 函数 | 描述 |
|
|
|
-| -------------------------------------------------------------------- | ------------------------------------------------------ |
|
|
|
+| 函数 | 描述 |
|
|
|
+| ------------------------------------------------------------------ | ------------------------------------------------------ |
|
|
|
| `void read_lock(rwlock_t *lock)` | 获取读锁 |
|
|
|
| `void read_unlock(rwlock_t *lock)` | 释放读锁 |
|
|
|
| `void read_lock_irq(rwlock_t *lock)` | 禁止本地中断,并且获取读锁 |
|
|
|
@@ -517,8 +531,8 @@ DEFINE_RWLOCK(my_rwlock);
|
|
|
|
|
|
**写锁 API**(排他)——原书表 47.3.3.1:
|
|
|
|
|
|
-| 函数 | 描述 |
|
|
|
-| --------------------------------------------------------------------- | ------------------------------------------------------ |
|
|
|
+| 函数 | 描述 |
|
|
|
+| ------------------------------------------------------------------- | ------------------------------------------------------ |
|
|
|
| `void write_lock(rwlock_t *lock)` | 获取写锁 |
|
|
|
| `void write_unlock(rwlock_t *lock)` | 释放写锁 |
|
|
|
| `void write_lock_irq(rwlock_t *lock)` | 禁止本地中断,并且获取写锁 |
|
|
|
@@ -598,16 +612,16 @@ MODULE_LICENSE("GPL");
|
|
|
|
|
|
### 6.5 读写锁 vs 自旋锁对比
|
|
|
|
|
|
-| 特性 | 读写锁(rwlock_t) | 自旋锁(spinlock_t) |
|
|
|
-| -------------------- | ------------------------------- | -------------------- |
|
|
|
-| **并发性** | 读者可并发 | 完全互斥 |
|
|
|
-| **实现开销** | 稍大(维护读者计数) | 更小 |
|
|
|
-| **写者开销** | 稍大 | 小 |
|
|
|
-| **中断上下文** | ✅ 可用 | ✅ 可用 |
|
|
|
-| **可睡眠** | ❌ 不可以 | ❌ 不可以 |
|
|
|
-| **适用场景** | 读多写少(读:写 > 10:1) | 读写均衡或写为主 |
|
|
|
+| 特性 | 读写锁(rwlock_t) | 自旋锁(spinlock_t) |
|
|
|
+| -------------- | ----------------------------- | -------------------- |
|
|
|
+| **并发性** | 读者可并发 | 完全互斥 |
|
|
|
+| **实现开销** | 稍大(维护读者计数) | 更小 |
|
|
|
+| **写者开销** | 稍大 | 小 |
|
|
|
+| **中断上下文** | ✅ 可用 | ✅ 可用 |
|
|
|
+| **可睡眠** | ❌ 不可以 | ❌ 不可以 |
|
|
|
+| **适用场景** | 读多写少(读:写 > 10:1) | 读写均衡或写为主 |
|
|
|
| **饥饿风险** | ⚠️ 读者持续涌入时写者可能饥饿 | 无 |
|
|
|
-| **优先级继承** | ❌ 不支持 | ❌ 不支持 |
|
|
|
+| **优先级继承** | ❌ 不支持 | ❌ 不支持 |
|
|
|
|
|
|
### 6.6 读写锁注意事项
|
|
|
|
|
|
@@ -689,11 +703,10 @@ seqlock_init(&my_seqlock);
|
|
|
DEFINE_SEQLOCK(my_seqlock);
|
|
|
```
|
|
|
|
|
|
-
|
|
|
**顺序锁写操作 API**(原书表 47.3.3.2):
|
|
|
|
|
|
-| 函数 | 描述 |
|
|
|
-| ----------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
|
+| 函数 | 描述 |
|
|
|
+| --------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
|
| `void write_seqlock(seqlock_t *sl)` | 获取写顺序锁 |
|
|
|
| `void write_sequnlock(seqlock_t *sl)` | 释放写顺序锁 |
|
|
|
| `void write_seqlock_irq(seqlock_t *sl)` | 禁止本地中断,并且获取写顺序锁 |
|
|
|
@@ -705,8 +718,8 @@ DEFINE_SEQLOCK(my_seqlock);
|
|
|
|
|
|
**顺序锁读操作 API**(原书表 47.3.3.2):
|
|
|
|
|
|
-| 函数 | 描述 |
|
|
|
-| --------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
|
+| 函数 | 描述 |
|
|
|
+| ------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
|
| `unsigned read_seqbegin(const seqlock_t *sl)` | 读单元访问共享资源的时候调用此函数,此函数会返回顺序锁的顺序号 |
|
|
|
| `unsigned read_seqretry(const seqlock_t *sl, unsigned start)` | 读结束以后调用此函数检查在读的过程中有没有对资源进行写操作,如果有的话就要重读 |
|
|
|
|
|
|
@@ -772,16 +785,16 @@ static ssize_t sensor_read(struct file *filp, char __user *buf,
|
|
|
|
|
|
### 7.4 顺序锁 vs 读写锁对比
|
|
|
|
|
|
-| 特性 | 顺序锁(seqlock) | 读写锁(rwlock) |
|
|
|
-| ------------------------ | -------------------------------- | ----------------------- |
|
|
|
-| **读者加锁** | ❌ 完全不加锁 | ✅ 需要加读锁 |
|
|
|
-| **读者开销** | 极低(2 次读序号) | 中(原子操作) |
|
|
|
-| **读者可能重试** | ✅ 会被写者打断后重试 | ❌ 不会 |
|
|
|
-| **写者饥饿** | ❌ 不会(写者优先) | ⚠️ 可能 |
|
|
|
+| 特性 | 顺序锁(seqlock) | 读写锁(rwlock) |
|
|
|
+| ------------------ | -------------------------- | ----------------------- |
|
|
|
+| **读者加锁** | ❌ 完全不加锁 | ✅ 需要加读锁 |
|
|
|
+| **读者开销** | 极低(2 次读序号) | 中(原子操作) |
|
|
|
+| **读者可能重试** | ✅ 会被写者打断后重试 | ❌ 不会 |
|
|
|
+| **写者饥饿** | ❌ 不会(写者优先) | ⚠️ 可能 |
|
|
|
| **可保护的数据** | 仅限**不含指针**的简单数据 | 任意数据(含链表/指针) |
|
|
|
-| **保护指针的危险** | ⚠️ 读侧可能读到野指针 | ✅ 安全 |
|
|
|
-| **典型场景** | jiffies、时间戳、统计计数 | 配置表、缓存 |
|
|
|
-| **是否可睡眠** | ❌ 写侧不可睡眠 | ❌ 不可睡眠 |
|
|
|
+| **保护指针的危险** | ⚠️ 读侧可能读到野指针 | ✅ 安全 |
|
|
|
+| **典型场景** | jiffies、时间戳、统计计数 | 配置表、缓存 |
|
|
|
+| **是否可睡眠** | ❌ 写侧不可睡眠 | ❌ 不可睡眠 |
|
|
|
|
|
|
> ⚠️ **关键限制**:顺序锁的读侧**不能保护指针**。因为读者在无锁读取指针时,写者可能已经释放(free)了指针指向的内存,读者解引用就是**野指针访问**。所以顺序锁只能用于"纯值类型"的数据(整数、结构体内全是整数)。
|
|
|
|
|
|
@@ -859,11 +872,11 @@ kfree_rcu(ptr, rcu_head_field); /* 延迟释放 */
|
|
|
|
|
|
**RCU 链表 API**:
|
|
|
|
|
|
-| 函数 | 说明 |
|
|
|
-| ---------------------------------------------- | -------------------------------------- |
|
|
|
-| `list_add_rcu(new, head)` | RCU 版链表插入 |
|
|
|
-| `list_del_rcu(entry)` | RCU 版链表删除(不释放内存,仅断链) |
|
|
|
-| `list_replace_rcu(old, new)` | RCU 版节点替换 |
|
|
|
+| 函数 | 说明 |
|
|
|
+| -------------------------------------------- | ------------------------------------ |
|
|
|
+| `list_add_rcu(new, head)` | RCU 版链表插入 |
|
|
|
+| `list_del_rcu(entry)` | RCU 版链表删除(不释放内存,仅断链) |
|
|
|
+| `list_replace_rcu(old, new)` | RCU 版节点替换 |
|
|
|
| `list_for_each_entry_rcu(pos, head, member)` | RCU 版遍历(需在`rcu_read_lock` 内) |
|
|
|
|
|
|
### 8.3 RCU 使用示例
|
|
|
@@ -932,8 +945,8 @@ static void del_first_node(void)
|
|
|
|
|
|
### 8.4 RCU vs 读写锁 vs 顺序锁
|
|
|
|
|
|
-| 特性 | RCU | 顺序锁 | 读写锁 |
|
|
|
-| -------------------------- | ---------------------------------- | --------------- | -------------- |
|
|
|
+| 特性 | RCU | 顺序锁 | 读写锁 |
|
|
|
+| -------------------- | ---------------------------------- | --------------- | -------------- |
|
|
|
| **读者开销** | 极低(近乎零) | 极低(读序号) | 中(原子操作) |
|
|
|
| **读者会重试** | ❌ 不会 | ✅ 会 | ❌ 不会 |
|
|
|
| **读者是否阻塞写者** | ❌ 完全不阻塞 | ❌ 不阻塞 | ✅ 阻塞 |
|
|
|
@@ -998,16 +1011,16 @@ DECLARE_COMPLETION(my_completion);
|
|
|
DECLARE_COMPLETION_ONSTACK(my_completion); /* 栈上定义,用于局部等待 */
|
|
|
```
|
|
|
|
|
|
-| 函数 | 说明 |
|
|
|
-| ------------------------------------------------------------------------------------------ | ---------------------------------------- |
|
|
|
-| `void init_completion(struct completion *x)` | 初始化完成量(计数=0) |
|
|
|
-| `void reinit_completion(struct completion *x)` | 重置完成量(允许重复使用) |
|
|
|
-| `void wait_for_completion(struct completion *x)` | 等待完成,**不可被信号打断** |
|
|
|
+| 函数 | 说明 |
|
|
|
+| ---------------------------------------------------------------------------------------- | -------------------------------------- |
|
|
|
+| `void init_completion(struct completion *x)` | 初始化完成量(计数=0) |
|
|
|
+| `void reinit_completion(struct completion *x)` | 重置完成量(允许重复使用) |
|
|
|
+| `void wait_for_completion(struct completion *x)` | 等待完成,**不可被信号打断** |
|
|
|
| `int wait_for_completion_interruptible(struct completion *x)` | 可被信号打断,被打断返回`-ERESTARTSYS` |
|
|
|
-| `int wait_for_completion_killable(struct completion *x)` | 可被致命信号打断 |
|
|
|
-| `unsigned long wait_for_completion_timeout(struct completion *x, unsigned long timeout)` | 带超时等待,超时返回 0 |
|
|
|
-| `void complete(struct completion *x)` | 唤醒**一个**等待者(计数+1) |
|
|
|
-| `void complete_all(struct completion *x)` | 唤醒**所有**等待者 |
|
|
|
+| `int wait_for_completion_killable(struct completion *x)` | 可被致命信号打断 |
|
|
|
+| `unsigned long wait_for_completion_timeout(struct completion *x, unsigned long timeout)` | 带超时等待,超时返回 0 |
|
|
|
+| `void complete(struct completion *x)` | 唤醒**一个**等待者(计数+1) |
|
|
|
+| `void complete_all(struct completion *x)` | 唤醒**所有**等待者 |
|
|
|
|
|
|
### 9.3 完成量使用示例
|
|
|
|
|
|
@@ -1057,16 +1070,16 @@ static int __init completion_demo_init(void)
|
|
|
|
|
|
### 9.4 完成量 vs 信号量对比
|
|
|
|
|
|
-| 特性 | 完成量(completion) | 信号量(semaphore) |
|
|
|
-| ------------------------ | -------------------------- | ------------------------------ |
|
|
|
-| **语义** | "等待事件完成" | "资源计数 / 互斥" |
|
|
|
+| 特性 | 完成量(completion) | 信号量(semaphore) |
|
|
|
+| ------------------ | -------------------------- | ---------------------------- |
|
|
|
+| **语义** | "等待事件完成" | "资源计数 / 互斥" |
|
|
|
| **初始值** | 固定为 0 | 可任意(`sema_init(&s, n)`) |
|
|
|
-| **主要用途** | 一对一/一对多事件通知 | 资源池、互斥 |
|
|
|
-| **能否重复使用** | 需`reinit_completion()` | 天然支持 |
|
|
|
-| **是否可睡眠** | ✅ 可以 | ✅ 可以 |
|
|
|
-| **中断上下文使用** | ✅ 只能`complete()` | ✅ 只能`up()` |
|
|
|
-| **代码可读性** | 语义更明确 | 需要看初值才知道意图 |
|
|
|
-| **典型场景** | 等待硬件就绪、等待线程退出 | 限制并发数、简单互斥 |
|
|
|
+| **主要用途** | 一对一/一对多事件通知 | 资源池、互斥 |
|
|
|
+| **能否重复使用** | 需`reinit_completion()` | 天然支持 |
|
|
|
+| **是否可睡眠** | ✅ 可以 | ✅ 可以 |
|
|
|
+| **中断上下文使用** | ✅ 只能`complete()` | ✅ 只能`up()` |
|
|
|
+| **代码可读性** | 语义更明确 | 需要看初值才知道意图 |
|
|
|
+| **典型场景** | 等待硬件就绪、等待线程退出 | 限制并发数、简单互斥 |
|
|
|
|
|
|
> 💡 **选择建议**:如果只是想"等某件事做完",用**完成量**比信号量语义更清晰、更不容易出错;如果需要"限制最多 N 个并发"或"互斥",用信号量或互斥体。
|
|
|
|
|
|
@@ -1280,8 +1293,8 @@ MODULE_AUTHOR("zuozhongkai");
|
|
|
|
|
|
### 10.2 代码逐行解析
|
|
|
|
|
|
-| 行号 | 代码 | 说明 |
|
|
|
-| -------------------------------------- | ---------------------- | -------------------- |
|
|
|
+| 行号 | 代码 | 说明 |
|
|
|
+| ------------------------------------ | ---------------------- | -------------------- |
|
|
|
| `atomic_t lock` | 设备结构体中的原子变量 | 用于实现设备互斥访问 |
|
|
|
| `atomic_set(&gpioled.lock, 1)` | 初始化原子变量为1 | 表示设备可用 |
|
|
|
| `atomic_dec_and_test(&gpioled.lock)` | 原子减1并测试是否为0 | 如果为0表示获取成功 |
|
|
|
@@ -1473,20 +1486,20 @@ gantt
|
|
|
|
|
|
### 12.1 IMX6ULL vs STM32 vs RK3568并发机制差异
|
|
|
|
|
|
-| 特性 | IMX6ULL | STM32 | RK3568 |
|
|
|
-| -------------------------- | ------------------ | ------------- | ------------------------- |
|
|
|
-| **CPU架构** | Cortex-A7 单核 | Cortex-M4/M7 | Cortex-A55 四核 |
|
|
|
-| **并发模型** | 单核+中断 | 单核+中断 | 多核SMP+中断 |
|
|
|
-| **自旋锁** | ✅ 支持 | ❌ 不需要 | ✅ 核间同步 |
|
|
|
-| **原子操作** | ✅ 支持 | ⚠️ 部分支持 | ✅ 支持 |
|
|
|
-| **互斥体** | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
|
|
|
-| **信号量** | ✅ 支持 | ✅ 支持 | ✅ 支持 |
|
|
|
-| **读写锁** | ✅ 支持 | ❌ 不需要 | ✅ 支持 |
|
|
|
-| **顺序锁** | ✅ 支持 | ❌ 不需要 | ✅ 支持 |
|
|
|
-| **RCU** | ✅ 支持 | ❌ 不需要 | ✅ 支持(重点) |
|
|
|
-| **中断优先级** | 32级 | 16级 | 32级 |
|
|
|
-| **典型锁选择** | spinlock_irqsave | 临界区 | mutex/spinlock |
|
|
|
-| **其他类型锁的使用** | 内核为主,驱动少用 | 无对应机制 | RCU 广泛用于网络/文件系统 |
|
|
|
+| 特性 | IMX6ULL | STM32 | RK3568 |
|
|
|
+| -------------------- | ------------------ | ------------ | ------------------------- |
|
|
|
+| **CPU架构** | Cortex-A7 单核 | Cortex-M4/M7 | Cortex-A55 四核 |
|
|
|
+| **并发模型** | 单核+中断 | 单核+中断 | 多核SMP+中断 |
|
|
|
+| **自旋锁** | ✅ 支持 | ❌ 不需要 | ✅ 核间同步 |
|
|
|
+| **原子操作** | ✅ 支持 | ⚠️ 部分支持 | ✅ 支持 |
|
|
|
+| **互斥体** | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
|
|
|
+| **信号量** | ✅ 支持 | ✅ 支持 | ✅ 支持 |
|
|
|
+| **读写锁** | ✅ 支持 | ❌ 不需要 | ✅ 支持 |
|
|
|
+| **顺序锁** | ✅ 支持 | ❌ 不需要 | ✅ 支持 |
|
|
|
+| **RCU** | ✅ 支持 | ❌ 不需要 | ✅ 支持(重点) |
|
|
|
+| **中断优先级** | 32级 | 16级 | 32级 |
|
|
|
+| **典型锁选择** | spinlock_irqsave | 临界区 | mutex/spinlock |
|
|
|
+| **其他类型锁的使用** | 内核为主,驱动少用 | 无对应机制 | RCU 广泛用于网络/文件系统 |
|
|
|
|
|
|
### 12.2 多核 vs 单核的锁选择
|
|
|
|
|
|
@@ -1526,7 +1539,7 @@ flowchart TD
|
|
|
| 等待方式 | 忙等待(占用CPU) | 睡眠(释放CPU) |
|
|
|
| 可睡眠 | ❌ 不可以 | ✅ 可以 |
|
|
|
| 中断上下文 | ✅ 可以使用 | ❌ 不可以 |
|
|
|
-| 临界区长度 | 短(μs级) | 长(ms级) |
|
|
|
+| 临界区长度 | 短(μs级) | 长(ms级) |
|
|
|
| CPU占用 | 高(忙等) | 低(上下文切换) |
|
|
|
| 适用场景 | 中断处理、短临界区 | 长时间操作、I/O |
|
|
|
|