任务同步与通信
2026/7/16大约 8 分钟内核组件内核同步通信IPC
任务同步与通信
📚 本节导读
学习时长: 约 90 分钟
难度级别: ⭐⭐⭐⭐☆
前置知识: 任务管理、中断管理、C 语言基础
在多任务实时操作系统中,任务之间需要协调对共享资源的访问,也需要在任务之间传递数据。OneOS 提供了丰富且完整的 IPC(Inter-Process Communication,进程间通信) 机制,涵盖同步原语和通信机制两大类。
本节是对 OneOS 所有 IPC 机制的全景概述,帮助你在实际开发中快速选择合适的机制。
🎯 学习目标
- 理解 OneOS 中各类 IPC 机制的分类和适用场景
- 掌握信号量、互斥锁、事件、消息队列、邮箱、自旋锁、工作队列的核心区别
- 能够根据实际需求选择合适的同步或通信机制
- 理解 PRIO 与 FIFO 唤醒策略的含义
- 了解静态创建与动态创建两种模式的区别
一、IPC 机制全景
OneOS 提供了七种 IPC 机制,按功能可分为两大类:
┌─────────────────────────────────────────────────────────────────────┐
│ OneOS IPC 机制全景 │
├──────────────────────────┬──────────────────────────────────────────┤
│ 同步机制 (Synchronization) │ 通信机制 (Communication) │
├──────────────────────────┼──────────────────────────────────────────┤
│ • 信号量 (Semaphore) │ • 事件 (Event) │
│ • 互斥锁 (Mutex) │ • 消息队列 (Message Queue) │
│ • 自旋锁 (Spinlock) │ • 邮箱 (Mailbox) │
│ │ │
│ 延迟执行 (Deferred Work) │ │
│ • 工作队列 (Workqueue) │ │
└──────────────────────────┴──────────────────────────────────────────┘二、核心机制对比
下表从多个维度对比了 OneOS 的全部 IPC 机制:
| 特性 | 信号量 | 互斥锁 | 事件 | 消息队列 | 邮箱 | 自旋锁 | 工作队列 |
|---|---|---|---|---|---|---|---|
| 用途 | 资源计数 / 同步 | 互斥访问共享资源 | 多条件同步 | 变长数据传递 | 指针值传递 | SMP 临界区保护 | 延迟工作执行 |
| 数据传递 | 无 | 无 | 32 位标志 | 数据副本(变长) | 指针值(固定大小) | 无 | 函数 + 参数 |
| 阻塞等待 | 是 | 是 | 是 | 是 | 是 | 否(忙等) | 否(异步) |
| 优先级继承 | 否 | 是 | 否 | 否 | 否 | 否 | 否 |
| 递归持有 | 否 | 支持 | 否 | 否 | 否 | 否 | 否 |
| 中断可用 | 仅 post | 否 | 仅 send | 否 | 否 | 是 | 否 |
| SMP 安全 | 内部保护 | 内部保护 | 内部保护 | 内部保护 | 内部保护 | 是 | 内部保护 |
| 一幅多收 | 否 | 否 | 是 | 是(一条消息一份) | 是(一条邮件一份) | 否 | 否 |
| P操作 | os_semaphore_wait | os_mutex_lock | os_event_recv | os_msgqueue_recv | os_mailbox_recv | os_spin_lock | os_work_run |
| V操作 | os_semaphore_post | os_mutex_unlock | os_event_send | os_msgqueue_send | os_mailbox_send | os_spin_unlock | — |
三、选型指南
3.1 何时使用信号量
- 资源计数:管理有限个数的资源(如缓冲区槽位、外设通道)
- 任务同步:一个任务等待另一个任务完成某个操作(二进制信号量)
- 中断与任务同步:中断中释放信号量,任务中等待信号量
3.2 何时使用互斥锁
- 共享资源互斥访问:保护全局变量、外设寄存器、数据结构等关键区域
- 需要优先级继承:防止优先级反转问题
- 需要递归锁:同一任务可以多次加锁
3.3 何时使用事件
- 多条件触发:一个任务等待多个事件条件中的任意一个(OR)或全部(AND)
- 一对多 / 多对多同步:一个事件源可以同时唤醒多个等待任务
- 状态标志传递:用 32 个位标记不同的系统状态
3.4 何时使用消息队列
- 变长数据传输:任务之间需要传递不同大小的数据块
- 数据缓冲:生产者-消费者模式,数据需要排队处理
- 数据拷贝:需要发送方和接收方各自持有独立数据副本
3.5 何时使用邮箱
- 指针值传递:传递内存地址、句柄或整数值,不需要拷贝数据本身
- 轻量通信:每次传递固定大小的值(
os_ubase_t),效率更高 - 固定容量缓冲:快速传递固定数量的消息
3.6 何时使用自旋锁
- SMP 多核保护:多核场景下保护极短的临界区
- 中断上下文:在中断中加锁(互斥锁不能在中断中使用)
- 极短临界区:临界区代码执行时间极短(微秒级),忙等开销小于上下文切换开销
3.7 何时使用工作队列
- 延迟执行:将工作推迟到工作线程中执行,释放当前上下文
- Bottom-Half 处理:中断上半部只做关键操作,将耗时操作交给工作队列
- 异步操作:不需要等待结果的操作,丢给工作队列即可
3.8 IPC 选型快速决策流程
面对同步/通信需求时,可按下面的决策流程快速定位最合适的机制:
你的需求是什么?
│
├─ 需要任务间传递数据吗?
│ ├─ 是,传变长数据块 ────────────► 消息队列
│ ├─ 是,传指针/句柄/整数(固定值)─► 邮箱
│ └─ 否
│ ├─ 需要保护共享资源吗?
│ │ ├─ 是,且临界区在中断中或极短(微秒级)──► 自旋锁
│ │ ├─ 是,普通任务上下文、需优先级继承 ──────► 互斥锁
│ │ └─ 否
│ │ ├─ 需要管理有限资源数量(计数)──────► 信号量
│ │ ├─ 需要等待多个条件(AND/OR)────────► 事件
│ │ ├─ 需要任务间简单同步(二值信号量)───► 信号量
│ │ └─ 需要把工作推迟/异步执行 ──────────► 工作队列
│ └─ ...一句话速记:
| 场景 | 首选机制 |
|---|---|
| 保护共享资源 + 优先级继承 | 互斥锁 |
| 保护极短临界区 / 中断上下文 | 自旋锁 |
| 资源计数 / 任务同步 | 信号量 |
| 多条件触发 | 事件 |
| 传递变长数据 | 消息队列 |
| 传递指针 / 固定值 | 邮箱 |
| 延迟 / 异步执行 | 工作队列 |
四、唤醒策略:PRIO vs FIFO
OneOS 的同步对象在释放时,如果有多个任务正在等待,需要决定唤醒哪个任务。每种机制都支持两种唤醒策略:
| 策略 | 宏定义值 | 含义 |
|---|---|---|
| PRIO(优先级) | 0x55 | 唤醒等待队列中优先级最高的任务。这是默认策略 |
| FIFO(先进先出) | 0xAA | 唤醒等待队列中等待时间最长的任务 |
设置唤醒策略的 API:
os_semaphore_set_wake_type(sem_id, OS_SEM_WAKE_TYPE_PRIO);
os_mutex_set_wake_type(mutex_id, OS_MUTEX_WAKE_TYPE_FIFO);
os_event_set_wake_type(event_id, OS_EVENT_WAKE_TYPE_PRIO);
os_msgqueue_set_wake_type(mq_id, OS_MQ_WAKE_TYPE_PRIO);
os_mailbox_set_wake_type(mb_id, OS_MB_WAKE_TYPE_PRIO);注意:自旋锁和工作队列不涉及等待任务队列,因此没有唤醒策略的概念。
五、静态创建与动态创建
OneOS 支持两种对象创建方式:
┌──────────────────────────────────────────────────────────────────────┐
│ 两种创建方式对比 │
├──────────────────────┬───────────────────────────────────────────────┤
│ 动态创建 (Dynamic) │ 静态创建 (Static) │
├──────────────────────┼───────────────────────────────────────────────┤
│ 运行时从堆分配内存 │ 编译时确定内存,使用全局变量 │
│ 需要启用 OS_USING_HEAP│ 不需要堆支持 │
│ 灵活,可动态创建/销毁 │ 确定性高,无内存碎片风险 │
│ 适合资源动态变化场景 │ 适合资源受限、确定性要求高的场景 │
└──────────────────────┴───────────────────────────────────────────────┘静态定义宏:
| 机制 | 静态定义宏 | 静态创建函数 |
|---|---|---|
| 信号量 | OS_SEMAPHORE_DEFINE(name) | os_semaphore_create(&cb, ...) |
| 互斥锁 | OS_MUTEX_DEFINE(name) | os_mutex_create(&cb, ...) |
| 事件 | OS_EVENT_DEFINE(name) | os_event_create(&cb, ...) |
| 消息队列 | OS_MSGQUEUE_DEFINE(name) + OS_MSGQUEUE_POOL_DEFINE(name, count, size) | os_msgqueue_create_static(&cb, ...) |
| 邮箱 | OS_MAILBOX_DEFINE(name) + OS_MAILBOX_POOL_DEFINE(name, count) | os_mailbox_create_static(&cb, ...) |
| 自旋锁 | OS_DEFINE_SPINLOCK(var) | os_spin_lock_init(&lock) |
| 工作队列 | OS_WORKQUEUE_DEFINE(name) | os_workqueue_create(&cb, ...) |
| 工作项 | OS_WORK_DEFINE(name) | os_work_init(&work, ...) |
六、子章节导航
| 章节 | 标题 | 核心内容 |
|---|---|---|
| 4.3.1 | 信号量 | 计数信号量、二值信号量、资源管理 |
| 4.3.2 | 互斥锁 | 互斥访问、优先级继承、递归锁 |
| 4.3.3 | 事件 | 32 位事件标志、AND/OR 触发 |
| 4.3.4 | 消息队列 | 变长数据传输、紧急发送 |
| 4.3.5 | 邮箱 | 指针值传递、轻量级通信 |
| 4.3.6 | 自旋锁 | SMP 临界区保护、中断安全 |
| 4.3.7 | 工作队列 | 延迟工作执行、Bottom-Half |
📝 本节小结
核心要点回顾
OneOS 提供 7 种 IPC 机制,覆盖嵌入式 RTOS 中常见的同步与通信需求:
1. 按功能分类
| 类别 | 机制 | 核心用途 |
|---|---|---|
| 同步 | 信号量 | 资源计数 / 任务同步 |
| 同步 | 互斥锁 | 共享资源互斥访问(支持优先级继承) |
| 同步 | 自旋锁 | SMP 极短临界区 / 中断上下文保护 |
| 通信 | 事件 | 多条件触发(32 位标志) |
| 通信 | 消息队列 | 变长数据传递(数据副本) |
| 通信 | 邮箱 | 指针值传递(固定大小) |
| 延迟执行 | 工作队列 | 异步延迟工作 / Bottom-Half |
2. 两个核心维度
- 唤醒策略:PRIO(按优先级唤醒,默认)/ FIFO(按等待时间唤醒),各机制通过
os_xxx_set_wake_type()设置。 - 创建方式:动态(运行时从堆分配,需
OS_USING_HEAP)/ 静态(编译时全局变量,无需堆)。
3. 选型核心原则
- 保护共享资源 → 互斥锁(需优先级继承)或自旋锁(极短临界区/中断上下文)
- 资源计数 → 信号量
- 多条件同步 → 事件
- 传递数据 → 消息队列(变长)或邮箱(指针值)
- 延迟执行 → 工作队列
关键易错点速查
| 易错点 | 正确做法 |
|---|---|
| 在中断中使用互斥锁 | 互斥锁禁止在中断中使用(阻塞 + 优先级继承),中断中应用信号量 post 或自旋锁 |
| 在中断中调用阻塞 API | 中断中只能调用不阻塞的接口(如 os_semaphore_post),不能 os_semaphore_wait |
| 用互斥锁做资源计数 | 互斥锁是二值互斥,计数/同步用信号量 |
| 用自旋锁保护长临界区 | 自旋锁忙等浪费 CPU,仅适用极短临界区 |
| 忘记配置唤醒策略 | 默认 PRIO;需要 FIFO 时显式调用 os_xxx_set_wake_type(OS_..._WAKE_TYPE_FIFO) |
| 动态创建但未启用堆 | 动态创建需 OS_USING_HEAP,否则应使用静态定义宏 |
下一步
本节是 IPC 机制的全景概述,帮助你建立了整体认识。接下来请学习下一小节,逐个深入每种机制:
- 4.3.1 信号量:最常用、最灵活的同步与资源计数机制