Przeglądaj źródła

wiki: 重写14-任务通知概念部分,补充原理/工作流/限制/选型对比

OpenCode 1 dzień temu
rodzic
commit
bf77fcafe0

+ 69 - 20
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/FreeRTOS学习笔记/14-任务通知.md

@@ -12,34 +12,83 @@ created: 2026-07-17
 
 ## 概念介绍
 
-### 基本概念
+### 为什么需要任务通知
 
-任务通知(Task Notification)是 FreeRTOS V8.2.0 引入的轻量级任务间通信机制。每个 TCB 中内置了一个 **32 位通知值**和 **8 位通知状态**,无需创建额外的内核对象。
+FreeRTOS 已经有队列、信号量、事件标志组这些 IPC 机制,为什么还需要任务通知?
 
-- **无需中间对象**:发送任务直接向目标任务发通知
-- **更快**:比队列/信号量省去了创建和操作中间对象的时间
-- **一对一**:每个任务只有一个通知值,不能一对多广播
+看一个常见场景:任务 A 做完某件事后,想告诉任务 B"活干完了,该你了"。用队列实现需要先 `xQueueCreate` 创建队列对象,然后 A 调用 `xQueueSend`、B 调用 `xQueueReceive`。用信号量实现也需要先 `xSemaphoreCreateBinary` 创建信号量对象。
 
-### 工作原理
+**这些 IPC 机制的问题是"中间商赚差价"**:每次通信都要经过一个额外创建的内核对象(队列/信号量/事件组)。对于"我就想叫一下隔壁任务"这么简单的事,创建一个完整的内核对象太浪费了。
 
-任务通知支持 3 种使用模式:
+任务通知就是为解决这个问题而生——**发送任务直接修改目标任务的 TCB 字段,不需要任何中间对象**。
 
-| 模式                     | 发送函数                                               | 接收函数               | 行为                                 |
-| ------------------------ | ------------------------------------------------------ | ---------------------- | ------------------------------------ |
-| **信号量模式**     | `xTaskNotifyGive()`                                  | `ulTaskNotifyTake()` | 通知值递增/递减,模拟二值/计数信号量 |
-| **消息邮箱模式**   | `xTaskNotify(handle, value, eSetValueWithOverwrite)` | `xTaskNotifyWait()`  | 直接覆盖通知值,模拟消息邮箱         |
-| **事件标志组模式** | `xTaskNotify(handle, bits, eSetBits)`                | `xTaskNotifyWait()`  | 按位 OR,模拟事件标志组              |
+### 基本原理
+
+每个 FreeRTOS 任务的 TCB(任务控制块)内部自带两个字段:
+
+- **`ulNotifiedValue`**:一个 32 位的通知值(可作计数器、位图、消息存储)
+- **`ucNotifyState`**:一个 8 位的通知状态(`taskNOT_WAITING_NOTIFICATION` / `taskWAITING_NOTIFICATION` / `taskNOTIFICATION_RECEIVED`)
+
+> `configTASK_NOTIFICATION_ARRAY_ENTRIES` 控制通知数组大小,默认 1。设为 N 时每个任务拥有 N 组独立通知值。FreeRTOS V10.4.0 之后支持此特性。
+
+**普通 IPC 的通信路径**:
+```
+任务 A → [创建队列/信号量对象] → 放入数据 → 队列/信号量 → 任务 B 取出数据
+                                 └── 中间对象(额外分配内存)──┘
+```
+
+**任务通知的通信路径**:
+```
+任务 A → 直接写入任务 B 的 TCB
+         └── 不创建任何对象 ──┘
+```
+
+这就是任务通知比队列/信号量快约 **45%** 的原因——省去了创建、操作、销毁内核对象的所有开销。
+
+### 通知的工作流程
+
+任务通知有两步:**发送**和**接收**。关键在于**接收方可以选择阻塞等待通知的到来**:
+
+1. **发送方**调用 `xTaskNotifyGive()` 或 `xTaskNotify()` → 直接写入目标任务的 TCB 通知值 → 将目标任务的通知状态标记为"已收到通知"
+2. **接收方**调用 `ulTaskNotifyTake()` 或 `xTaskNotifyWait()`:
+   - 如果已经有**挂起的通知**(之前有人给它发过通知但还没处理)→ 立即返回,处理通知值
+   - 如果**没有挂起的通知** → 任务进入阻塞态,等待通知到来
+3. 当发送方再次给这个任务发送通知时,接收方解除阻塞,处理通知值
+
+### 三种使用模式
+
+通知值的 32 位可以灵活解释,由此衍生出三种使用模式,替代三种传统 IPC:
+
+| 模式 | 对应传统 IPC | 发送函数 | 接收函数 | 32 位值的用法 |
+|------|-------------|---------|---------|--------------|
+| **信号量模式** | 二值/计数信号量 | `xTaskNotifyGive()` | `ulTaskNotifyTake()` | 当作计数器:Give 加 1,Take 减 1 |
+| **消息邮箱模式** | 队列(单消息) | `xTaskNotify(..., eSetValueWithOverwrite)` | `xTaskNotifyWait()` | 当作消息容器:覆写/条件覆写 32 位值 |
+| **事件标志组模式** | 事件标志组 | `xTaskNotify(..., eSetBits)` | `xTaskNotifyWait()` | 当作位图:按位 OR 设置标志位 |
+
+**一句话选型**:
+- 只需通知对方"事件发生了" → 信号量模式(最轻量)
+- 需要传递一个数值给对方 → 消息邮箱模式
+- 需要多个条件的组合触发 → 事件标志组模式
+
+### 核心劣势(与传统 IPC 对比)
+
+| 对比项 | 队列/信号量/事件组 | 任务通知 |
+|--------|-------------------|---------|
+| 通信方向 | 多对多(多个任务可发/收) | 只能一对一(一个任务只有一个通知值) |
+| 数据缓存 | 队列可缓存多条消息 | 只能缓存一个 32 位值(覆写会丢数据) |
+| ISR 接收 | 可接收 | **不能接收**(无 FromISR 版本接收函数) |
+| 创建步骤 | 需先 Create 对象 | **无需创建**,直接用 |
 
 ### 核心API
 
-| 函数                         | 参数                                                                                          | 功能简述                                     |
-| ---------------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------- |
-| `xTaskNotifyGive()`        | `xTaskToNotify`                                                                             | 发送通知(信号量模式),通知值 +1            |
-| `ulTaskNotifyTake()`       | `xClearCountOnExit`, `xTicksToWait`                                                       | 接收通知(信号量模式),返回通知值           |
-| `xTaskNotify()`            | `xTaskToNotify`, `ulValue`, `eAction`                                                   | 发送通知(带值),eAction 控制写/OR/覆写行为 |
-| `xTaskNotifyWait()`        | `ulBitsToClearOnEntry`, `ulBitsToClearOnExit`, `pulNotificationValue`, `xTicksToWait` | 接收通知(带值),可获取通知值和清除指定位   |
-| `xTaskNotifyFromISR()`     | 同上+ISR 版本                                                                                 | 中断中发送通知                               |
-| `vTaskNotifyGiveFromISR()` | `xTaskToNotify`, `pxHigherPriorityTaskWoken`                                              | 中断中发送(信号量模式)                     |
+| 函数 | 参数 | 功能简述 |
+|------|------|---------|
+| `xTaskNotifyGive()` | `xTaskToNotify` | 发送通知(信号量模式),通知值 +1 |
+| `ulTaskNotifyTake()` | `xClearCountOnExit`, `xTicksToWait` | 接收通知(信号量模式),返回通知值 |
+| `xTaskNotify()` | `xTaskToNotify`, `ulValue`, `eAction` | 发送通知(带值),eAction 控制写/OR/覆写行为 |
+| `xTaskNotifyWait()` | `ulBitsToClearOnEntry`, `ulBitsToClearOnExit`, `pulNotificationValue`, `xTicksToWait` | 接收通知(带值),可获取通知值和清除指定位 |
+| `xTaskNotifyFromISR()` | 同上+ISR 版本 | 中断中发送通知 |
+| `vTaskNotifyGiveFromISR()` | `xTaskToNotify`, `pxHigherPriorityTaskWoken` | 中断中发送(信号量模式) |
 
 ## 实验