# uCOS-II 源码详细分析


# uC/OS-II 源码详细分析（基于V2.92经典稳定版）
uC/OS-II 是由Jean J. Labrosse开发的**可剥夺型硬实时多任务RTOS内核**，专为资源受限的嵌入式系统设计，全部源码遵循ANSI C标准编写，仅少量CPU架构相关代码用汇编实现，具备可裁剪、可移植、可固化、调度时间恒定、实时性强的核心特点，广泛应用于工业控制、医疗设备、汽车电子等对确定性和可靠性有严苛要求的领域。

## 一、源码整体分层架构
uC/OS-II 采用严格的分层模块化设计，彻底解耦硬件相关逻辑与内核通用逻辑，极大提升了可移植性，源码整体分为三大核心层级：



### 1. 硬件无关层（内核通用核心，无需修改）
该层是uC/OS-II的核心主体，与CPU/硬件平台完全无关，跨平台通用，每个模块对应独立的C文件，职责清晰：
| 源文件 | 核心功能 |
|--------|----------|
| os_core.c | 内核中枢，实现初始化、任务调度、中断管理、临界区、TCB/ECB链表管理等核心逻辑 |
| os_task.c | 任务管理，实现任务创建/删除/挂起/恢复/优先级修改等专属API |
| os_time.c | 时间管理，实现时钟节拍处理、任务延时、延时恢复等时间相关功能 |
| os_mem.c | 内存管理，实现固定大小内存分区管理，解决内存碎片问题 |
| os_sem.c | 计数型/二值信号量，实现任务同步与资源互斥访问 |
| os_mutex.c | 互斥信号量，基于优先级继承协议解决优先级反转问题 |
| os_mbox.c | 消息邮箱，实现单指针消息的任务间通信 |
| os_q.c | 消息队列，邮箱的增强版，支持多消息FIFO异步通信 |
| os_flag.c | 事件标志组，支持多事件的逻辑与/或组合等待 |
| ucos_ii.h | 内核总头文件，定义所有数据结构、宏、枚举、函数声明 |
| ucos_ii.c | 内核入口，统一包含所有核心源文件，方便工程管理 |

### 2. 硬件相关层（移植层，需根据CPU/编译器修改）
该层是内核与硬件之间的抽象层，所有与CPU架构、编译器相关的代码均集中在此，是移植的核心修改对象：
| 源文件 | 核心功能 |
|--------|----------|
| os_cpu.h | CPU相关宏定义、基本数据类型、编译器配置、关/开中断宏、任务切换宏 |
| os_cpu_c.c | C语言实现的移植函数，核心是**任务栈初始化函数OSTaskStkInit()**，以及各类钩子函数 |
| os_cpu_a.asm | 汇编实现的底层核心函数，包括任务启动OSStartHighRdy()、任务级切换OSCtxSw()、中断级切换OSIntCtxSw() |

### 3. 用户配置层（可裁剪定制，按需修改）
通过宏开关实现内核功能的精细化裁剪与配置，无需修改内核源码，适配不同资源的硬件平台：
| 源文件 | 核心功能 |
|--------|----------|
| os_cfg.h | 内核配置文件，通过宏定义配置最大任务数、最大事件数、是否启用统计任务/模块裁剪、时钟节拍频率等核心参数 |
| includes.h | 工程总头文件，统一包含内核头文件、硬件驱动头文件、用户代码头文件，集中管理工程依赖 |

## 二、核心数据结构详解
理解uC/OS-II源码的核心是吃透三大基础数据结构，它们是整个内核运行的基石。

### 1. 任务控制块 OS_TCB（Task Control Block）
每个任务唯一对应一个OS_TCB结构体，是任务的“身份证”，内核通过TCB管理任务的全部状态与属性，所有TCB通过双向链表串联管理。核心源码定义如下：
```c
typedef struct os_tcb {
    OS_STK          *OSTCBStkPtr;           /* 栈顶指针，结构体第一个成员，汇编无需偏移即可访问，提升切换效率 */
    struct os_tcb   *OSTCBNext;             /* TCB双向链表后向指针 */
    struct os_tcb   *OSTCBPrev;             /* TCB双向链表前向指针 */
    OS_EVENT        *OSTCBEventPtr;         /* 指向任务等待的事件控制块ECB */
    void            *OSTCBMsg;              /* 任务接收的消息指针，邮箱/队列专用 */
    INT16U          OSTCBDly;               /* 任务延时节拍数/事件等待超时计数 */
    INT8U           OSTCBStat;               /* 任务状态：就绪/等待/挂起/休眠 */
    INT8U           OSTCBPrio;               /* 任务唯一优先级，0最高，OS_LOWEST_PRIO最低 */
    INT8U           OSTCBX;                  /* 就绪表辅助：优先级低3位，对应OSRdyTbl的位 */
    INT8U           OSTCBY;                  /* 就绪表辅助：优先级高3位，对应OSRdyTbl的行 */
    INT8U           OSTCBBitX;               /* 就绪表位掩码：1<<OSTCBX，预计算加速调度 */
    INT8U           OSTCBBitY;               /* 就绪表位掩码：1<<OSTCBY，预计算加速调度 */
#if OS_TASK_DEL_EN > 0
    INT8U           OSTCBDelReq;             /* 任务删除请求标记，支持安全删除 */
#endif
#if OS_TASK_SUSPEND_EN > 0
    INT8U           OSTCBSuspendCtr;         /* 任务挂起计数，支持嵌套挂起 */
#endif
    /* 其他裁剪相关可选成员，如事件标志组指针、任务名、栈检测参数等 */
} OS_TCB;
```
**核心设计细节**：
- 栈顶指针`OSTCBStkPtr`放在结构体首个地址，汇编语言访问时无需计算结构体偏移，大幅提升任务上下文切换的效率。
- `OSTCBX/Y/BitX/BitY`在任务创建时预计算完成，避免调度时重复运算，是实现O(1)调度的关键优化之一。
- 内核维护两个核心TCB链表：`OSTCBFreeList`（空闲TCB单向链表，创建任务时分配）、`OSTCBList`（已创建任务TCB双向链表，内核遍历管理）。

### 2. 就绪表与优先级位图算法
就绪表是uC/OS-II实现**O(1)级抢占式调度**的核心，无论系统中有多少任务，找到最高优先级就绪任务的时间恒定，完全满足硬实时系统的确定性要求。

就绪表由两个核心全局变量组成，定义在os_core.c中：
```c
INT8U  OSRdyGrp;                         /* 就绪组：8位，每一位对应OSRdyTbl的一行，该行有任务就绪则对应位为1 */
INT8U  OSRdyTbl[OS_RDY_TBL_SIZE];        /* 就绪表：8个8位元素，每一位对应一个优先级的就绪状态 */
```
**设计原理**：
- uC/OS-II默认支持64个优先级（0~63），用6位二进制即可表示一个优先级，高3位为Y（0~7），对应`OSRdyGrp`的位号和`OSRdyTbl`的行号；低3位为X（0~7），对应`OSRdyTbl[Y]`的位号。
- 优先级与位图的映射关系（任务创建时预计算）：
  ```c
  OSTCBY    = prio >> 3;    // 等价于 prio / 8，取高3位
  OSTCBX    = prio & 0x07;  // 等价于 prio % 8，取低3位
  OSTCBBitY = 1 << OSTCBY;  // 行掩码
  OSTCBBitX = 1 << OSTCBX;  // 位掩码
  ```
- 任务就绪操作（写入就绪表）：
  ```c
  OSRdyGrp |= OSTCBBitY;               // 标记该行有任务就绪
  OSRdyTbl[OSTCBY] |= OSTCBBitX;       // 标记该优先级任务就绪
  ```
- 任务取消就绪（从就绪表移除）：
  ```c
  if ((OSRdyTbl[OSTCBY] &= ~OSTCBBitX) == 0) {
      OSRdyGrp &= ~OSTCBBitY;  // 该行无就绪任务，清除组标记
  }
  ```

**最高优先级就绪任务快速查找**：
通过预定义的`OSUnMapTbl`映射表，仅需两次查表即可定位最高优先级，彻底避免遍历，实现O(1)调度。
```c
// 256字节映射表，预定义8位数值中最低位为1的位置，例如0x04(00000100)返回2
INT8U  const  OSUnMapTbl[256] = {
    0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};

// 核心查找逻辑，调度器核心
Y = OSUnMapTbl[OSRdyGrp];                  // 找到有就绪任务的最低行号
X = OSUnMapTbl[OSRdyTbl[Y]];               // 找到该行中就绪的最低位号
OSPrioHighRdy = (INT8U)((Y << 3) + X);     // 拼接得到最高优先级
```

### 3. 事件控制块 OS_EVENT（Event Control Block）
uC/OS-II的信号量、互斥锁、邮箱、消息队列**全部基于ECB统一管理**，实现了内核对象的复用，所有等待事件的任务通过ECB的等待表统一管理，设计逻辑与就绪表完全一致。
```c
typedef struct os_event {
    INT8U   OSEventType;                    /* 事件类型：信号量/互斥锁/邮箱/消息队列 */
    INT16U  OSEventCnt;                     /* 信号量计数器，互斥锁/邮箱/队列不使用 */
    void    *OSEventPtr;                    /* 邮箱消息指针/队列控制块指针 */
    INT8U   OSEventGrp;                     /* 事件等待组，与就绪表OSRdyGrp逻辑一致 */
    INT8U   OSEventTbl[OS_EVENT_TBL_SIZE];  /* 事件等待表，管理等待该事件的任务 */
#if OS_EVENT_NAME_EN > 0
    INT8U   *OSEventName;                   /* 事件名，调试用 */
#endif
} OS_EVENT;
```
**核心设计细节**：
- 内核维护`OSEventFreeList`空闲ECB单向链表，创建内核对象时分配，删除时归还，无动态内存分配，规避内存碎片风险。
- 事件等待表的操作逻辑与就绪表完全一致，事件发生时，通过`OSUnMapTbl`快速找到等待该事件的最高优先级任务，将其唤醒并放入就绪表，触发调度。

## 三、内核核心流程源码分析
### 1. 内核初始化 OSInit()
源码位于os_core.c，是uC/OS-II运行的第一步，**必须在OSStart()之前调用**，完成所有内核全局变量、数据结构、空闲链表的初始化，创建系统必备任务。

**核心执行流程**：
1.  初始化就绪表：`OSRdyGrp = 0`，`OSRdyTbl`数组全部清零，无任何任务就绪。
2.  初始化TCB空闲链表：根据`OS_MAX_TASKS`配置，创建对应数量的TCB，串联成单向空闲链表`OSTCBFreeList`。
3.  初始化ECB空闲链表：根据`OS_MAX_EVENTS`配置，创建对应数量的ECB，串联成单向空闲链表`OSEventFreeList`。
4.  初始化各模块空闲链表：内存管理、事件标志组、定时器等模块的空闲控制块链表（根据裁剪配置执行）。
5.  创建系统核心任务：
    - **空闲任务OS_TaskIdle()**：优先级固定为最低`OS_LOWEST_PRIO`，必须创建，当无其他就绪任务时运行，死循环执行，可通过钩子函数实现低功耗控制。
    - **统计任务OS_TaskStat()**：可选，通过`OS_TASK_STAT_EN`宏开启，优先级固定为`OS_LOWEST_PRIO-1`，用于统计CPU使用率、任务运行时间等系统状态。
6.  初始化全局变量：`OSRunning = OS_FALSE`（标记内核未启动）、中断嵌套计数器`OSIntNesting = 0`、调度器锁计数器`OSLockNesting = 0`等。
7.  初始化钩子函数：用户可自定义的任务切换、时钟节拍、任务创建/删除等钩子函数。

### 2. 内核启动 OSStart()
源码位于os_core.c，在`OSInit()`执行完成，且至少创建1个用户任务后调用，作用是启动多任务调度，正式将CPU控制权交给uC/OS-II内核，**该函数只会执行一次，且不会返回**。

**核心执行流程**：
1.  遍历就绪表，通过`OSUnMapTbl`找到当前就绪的最高优先级任务，赋值给`OSPrioHighRdy`和`OSPrioCur`。
2.  获取该最高优先级任务的TCB，赋值给`OSTCBHighRdy`和`OSTCBCur`。
3.  调用汇编函数`OSStartHighRdy()`，执行第一个任务的上下文切换：
    - 初始化系统时钟节拍定时器。
    - 从最高优先级任务的栈中恢复CPU寄存器上下文。
    - 跳转到任务函数入口，开始执行第一个任务。
4.  设置`OSRunning = OS_TRUE`，标记内核正式运行，调度器生效。

### 3. 任务级调度器 OSSched()
源码位于os_core.c，是uC/OS-II任务切换的核心入口，**仅用于任务级调度**，在任务主动放弃CPU时调用（如任务延时、等待事件、释放信号量、修改优先级等场景），核心职责是找到最高优先级就绪任务，并触发任务切换。

**核心执行流程**：
```c
void  OSSched (void)
{
    INT8U  y;

    OS_ENTER_CRITICAL();  // 进入临界区，关中断保护
    // 合法性检查：中断中不调度、调度器锁定时不调度
    if ((OSIntNesting == 0) && (OSLockNesting == 0)) {
        y = OSUnMapTbl[OSRdyGrp];
        OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
        // 最高优先级任务不是当前任务，才需要切换
        if (OSPrioHighRdy != OSPrioCur) {
            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
            OSCtxSwCtr++;  // 任务切换计数，统计用
            OSCtxSw();      // 调用汇编函数，执行任务级上下文切换
        }
    }
    OS_EXIT_CRITICAL();  // 退出临界区，开中断
}
```
**核心规则**：
- 调度器执行全程必须在临界区中，避免中断打断调度过程，导致就绪表、TCB等共享数据竞争。
- 中断上下文、调度器锁定状态下，禁止执行任务切换，保证系统稳定性。
- 仅当最高优先级就绪任务与当前任务不同时，才会触发上下文切换，避免无意义的性能损耗。

### 4. 任务上下文切换
任务切换的本质是**保存当前任务的CPU寄存器上下文到自身任务栈，再从新任务的任务栈中恢复寄存器上下文到CPU**，实现CPU使用权的转移，全部由汇编代码实现，位于os_cpu_a.asm中，分为两种场景。

#### 4.1 任务级切换 OSCtxSw()
由`OSSched()`调用，用于正常任务间的切换，以ARM Cortex-M架构为例，核心执行流程：
1.  触发PendSV异常（Cortex-M架构专用，可在中断末尾低优先级执行，避免打断其他中断，保证实时性）。
2.  PendSV异常处理中，CPU自动保存xPSR、PC、LR、R12、R3-R0寄存器到当前任务栈。
3.  手动保存R4-R11通用寄存器到当前任务栈，完成完整上下文保存。
4.  将当前任务的栈顶指针SP，保存到当前任务TCB的`OSTCBStkPtr`成员。
5.  更新全局变量：`OSTCBCur = OSTCBHighRdy`，`OSPrioCur = OSPrioHighRdy`。
6.  从新任务TCB的`OSTCBStkPtr`中加载栈顶指针到SP。
7.  从新任务栈中恢复R4-R11通用寄存器。
8.  执行异常返回，CPU自动从新任务栈中恢复xPSR、PC等寄存器，新任务正式开始运行。

#### 4.2 中断级切换 OSIntCtxSw()
由`OSIntExit()`调用，用于中断服务结束后触发的任务切换，与任务级切换的核心区别是：
中断发生时，CPU已经自动保存了当前任务的完整/部分上下文到任务栈，因此`OSIntCtxSw()`无需重复保存上下文，仅需完成后续的TCB更新、新任务上下文恢复流程，大幅减少中断处理的性能开销，提升中断响应速度。

## 四、核心功能模块源码详解
### 1. 任务管理模块（os_task.c）
任务是uC/OS-II的最小调度单位，每个任务是一个无限循环的函数，具备**唯一的静态优先级**，内核通过TCB对任务进行全生命周期管理。

#### 1.1 任务创建 OSTaskCreate()
基础任务创建函数，是任务生命周期的起点，核心源码执行流程：
1.  合法性校验：优先级是否超出范围、是否已被占用、栈指针是否合法、是否在内核运行后创建空闲任务。
2.  调用`OSTaskStkInit()`，初始化任务栈，模拟任务被中断后的栈结构，填充PC（任务函数入口地址）、LR（任务退出钩子函数）、寄存器初始值，返回初始化后的栈顶指针。
3.  从`OSTCBFreeList`中分配一个空闲TCB，若分配失败，返回错误码。
4.  初始化TCB的所有成员，包括栈顶指针、优先级、预计算的`OSTCBX/Y/BitX/BitY`、任务状态等。
5.  将新任务的TCB插入到`OSTCBList`双向链表的头部，同时将TCB地址存入`OSTCBPrioTbl[prio]`数组，通过优先级快速索引TCB。
6.  将新任务设置为就绪态，写入就绪表。
7.  若内核已运行（`OSRunning == OS_TRUE`），立即调用`OSSched()`触发调度，若新任务优先级更高，会立即抢占CPU运行。
8.  退出临界区，返回创建结果。

#### 1.2 核心任务管理API
| API函数 | 核心功能 | 调用限制 |
|---------|----------|----------|
| OSTaskCreateExt() | 扩展任务创建，支持栈检测、任务名、用户数据等扩展功能 | 任务级/启动前 |
| OSTaskDel() | 删除任务，将任务从就绪表/等待表移除，TCB归还空闲链表 | 仅任务级，禁止删除空闲任务 |
| OSTaskSuspend() | 强制挂起任务，任务进入挂起态，即使就绪也无法被调度 | 任务级，支持嵌套挂起 |
| OSTaskResume() | 恢复被挂起的任务，解除挂起态，就绪则写入就绪表 | 任务级 |
| OSTaskChangePrio() | 动态修改任务的优先级，同步更新就绪表和TCB预计算参数 | 任务级，禁止修改空闲任务优先级 |

### 2. 时间管理模块（os_time.c）
uC/OS-II的时间管理完全基于**时钟节拍（SysTick）**，时钟节拍是固定频率的硬件定时器中断，是系统的“心跳”，默认频率10~1000Hz，频率越高实时性越好，但系统开销越大。

#### 2.1 时钟节拍处理 OSTimeTick()
必须在时钟节拍中断服务函数中调用，是系统时间驱动的核心，源码执行流程：
1.  检查内核是否已运行（`OSRunning == OS_TRUE`），未运行则直接返回。
2.  调用`OSIntEnter()`，中断嵌套计数器`OSIntNesting`加1，标记进入中断上下文。
3.  遍历`OSTCBList`双向链表中的所有任务TCB：
    - 若任务的`OSTCBDly`（延时/超时计数）大于0，将其减1。
    - 若减1后`OSTCBDly == 0`，且任务无挂起、无等待事件，将该任务从等待态转为就绪态，写入就绪表。
4.  调用`OSTimeTickHook()`时钟节拍钩子函数，用户可自定义周期执行的逻辑。
5.  调用`OSIntExit()`，中断嵌套计数器减1，若所有中断处理完毕且有更高优先级任务就绪，触发中断级任务切换。

#### 2.2 任务延时 OSTimeDly()
任务主动放弃CPU的核心方式，将任务从就绪表移除，进入延时等待态，直到延时计数归零，源码执行流程：
1.  合法性校验：禁止在中断中调用、禁止延时空闲任务、延时节拍数不能为0。
2.  进入临界区，设置当前任务TCB的`OSTCBDly = ticks`（延时节拍数）。
3.  将当前任务从就绪表中清除，转为等待态。
4.  退出临界区，调用`OSSched()`触发调度，CPU切换到其他最高优先级就绪任务。
5.  延时结束后，任务被`OSTimeTick()`置为就绪态，重新获得CPU使用权，函数返回。

#### 2.3 核心时间管理API
| API函数 | 核心功能 | 调用限制 |
|---------|----------|----------|
| OSTimeDlyHMSM() | 按小时/分钟/秒/毫秒延时，内部转换为节拍数调用OSTimeDly() | 仅任务级 |
| OSTimeDlyResume() | 强制结束任务的延时，提前将其置为就绪态 | 仅任务级 |
| OSTimeGet()/OSTimeSet() | 获取/设置系统时钟节拍计数器，用于计时 | 无限制 |

### 3. 内存管理模块（os_mem.c）
uC/OS-II采用**固定大小内存分区管理**机制，彻底解决了嵌入式系统中标准malloc/free带来的内存碎片、分配时间不确定的问题，实现了O(1)级别的内存分配与释放。

#### 3.1 内存控制块 OS_MEM
每个内存分区对应一个OS_MEM结构体，管理一块连续的内存区域，划分为多个固定大小的内存块：
```c
typedef struct os_mem {
    void    *OSMemAddr;         /* 内存分区的起始地址 */
    void    *OSMemFreeList;     /* 空闲内存块单向链表头指针 */
    INT32U  OSMemBlkSize;       /* 每个内存块的字节大小 */
    INT32U  OSMemNBlks;         /* 分区内总内存块数量 */
    INT32U  OSMemNFree;         /* 分区内当前空闲内存块数量 */
#if OS_MEM_NAME_EN > 0
    INT8U   *OSMemName;         /* 内存分区名，调试用 */
#endif
} OS_MEM;
```

#### 3.2 核心内存管理API源码逻辑
| API函数 | 核心功能 | 执行核心流程 |
|---------|----------|--------------|
| OSMemCreate() | 创建内存分区，将连续内存划分为固定大小的内存块 | 1. 校验内存地址、块大小、块数量合法性；2. 从OSMemFreeList分配空闲OS_MEM；3. 将内存划分为多个块，串联成空闲链表；4. 初始化OS_MEM所有成员，返回分区指针 |
| OSMemGet() | 从分区申请一个内存块，O(1)复杂度 | 1. 校验分区指针和空闲块数量；2. 关中断，从空闲链表头部取出第一个空闲块；3. 更新空闲链表头指针，空闲计数减1；4. 开中断，返回内存块指针 |
| OSMemPut() | 释放内存块到对应分区，O(1)复杂度 | 1. 校验分区和内存块指针合法性；2. 关中断，将内存块插入空闲链表头部；3. 空闲计数加1；4. 开中断，返回执行结果 |

### 4. 同步与通信模块
uC/OS-II提供了完整的任务间同步与通信机制，全部基于ECB统一管理，覆盖信号量、互斥锁、邮箱、消息队列、事件标志组。

#### 4.1 信号量（os_sem.c）
用于任务间同步、共享资源互斥访问、中断与任务同步，核心是计数器机制，核心API：
- `OSSemCreate()`：创建信号量，初始化计数器，返回ECB指针。
- `OSSemPend()`：P操作，等待信号量，计数器>0则减1成功返回；否则任务进入等待态，加入事件等待表，设置超时时间。
- `OSSemPost()`：V操作，释放信号量，有等待任务则唤醒最高优先级任务，无等待任务则计数器加1。
- `OSSemDel()`：删除信号量，归还ECB到空闲链表。

#### 4.2 互斥信号量（os_mutex.c）
专为共享资源互斥访问设计，基于**优先级继承协议**，彻底解决普通信号量的优先级反转问题：当低优先级任务持有互斥锁，被高优先级任务等待时，临时将低优先级任务的优先级提升到等待该锁的最高优先级，避免中间优先级任务抢占。核心API：`OSMutexCreate()`、`OSMutexPend()`、`OSMutexPost()`。

#### 4.3 消息邮箱与消息队列
- **消息邮箱（os_mbox.c）**：实现单指针消息的传递，`OSEventPtr`指向消息内容，支持一对一/一对多通信，核心API：`OSMboxCreate()`、`OSMboxPend()`、`OSMboxPost()`。
- **消息队列（os_q.c）**：邮箱的增强版，支持多消息FIFO异步通信，基于环形缓冲区实现，支持多生产者-多消费者模型，核心API：`OSQCreate()`、`OSQPend()`、`OSQPost()`、`OSQPostFront()`。

### 5. 中断管理
uC/OS-II支持中断嵌套，最高嵌套层数由`OS_MAX_INT_NESTING`定义（默认255层），中断可抢占任务执行，中断服务结束后可触发任务切换，保证高优先级任务的实时响应。

#### 5.1 核心中断管理API
- `OSIntEnter()`：必须在中断服务函数开头调用，将中断嵌套计数器`OSIntNesting`加1，标记进入中断上下文，禁止任务级调度。
- `OSIntExit()`：必须在中断服务函数末尾调用，将`OSIntNesting`减1，当计数器归零（所有中断处理完毕）时，检查是否有更高优先级任务被唤醒，若有则调用`OSIntCtxSw()`执行中断级任务切换。

#### 5.2 中断服务函数标准模板
```c
void  XXX_IRQHandler(void)
{
    OSIntEnter();  // 进入中断，标记嵌套层数
    /* 中断核心业务处理 */
    // 1. 清除中断标志位，避免重复触发
    // 2. 读取硬件数据、执行最小化的硬件操作
    // 3. 可调用OSSemPost()/OSQPost()唤醒任务，禁止调用Pend类阻塞函数
    OSIntExit();   // 退出中断，检查并触发任务切换
}
```
**核心规则**：
- 中断服务函数中**绝对禁止调用阻塞类函数**（如OSTimeDly()、OSSemPend()），中断上下文无法被挂起。
- 中断服务函数应尽可能精简，耗时操作放到任务中执行，中断仅负责唤醒任务，保证中断响应延迟最小化。

## 五、临界区保护机制
uC/OS-II对就绪表、TCB、ECB等内核共享资源的访问，通过临界区保护，核心是关/开中断，避免多任务/中断并发访问导致的数据竞争与错乱。

核心宏定义在os_cpu.h中：
```c
#define  OS_ENTER_CRITICAL()  // 进入临界区，关中断
#define  OS_EXIT_CRITICAL()   // 退出临界区，开中断
```
提供三种可配置的实现方式，通过`OS_CRITICAL_METHOD`宏选择：
1.  **方式1**：直接关中断/开中断，实现最简单，但会破坏中断原有状态，不推荐使用。
2.  **方式2**：进入临界区前，将中断状态保存到局部变量，关中断；退出时从局部变量恢复中断状态，不会破坏原有中断状态，最常用。
3.  **方式3**：通过栈保存中断状态，进入前压栈保存中断状态，关中断；退出时出栈恢复，适用于编译器不支持局部变量保存状态寄存器的场景。

## 六、源码设计核心亮点与局限
### 1. 核心设计亮点
- **硬实时确定性**：基于位图的O(1)调度算法，调度时间恒定，与任务数量无关，完全满足硬实时系统要求。
- **极致可移植性**：严格的分层设计，硬件相关代码集中在3个文件，适配几乎所有8/16/32位MCU/MPU，移植门槛极低。
- **极简高可靠**：遵循“简洁即可靠”的设计理念，无动态内存分配，所有内核对象均为静态管理，彻底规避内存碎片、堆分配失败等非确定性风险。
- **强可裁剪性**：通过os_cfg.h的宏开关，可精细化裁剪不需要的模块，最小内核ROM占用可低至几KB，RAM占用几百字节，完美适配资源极度受限的嵌入式平台。
- **代码规范性极强**：命名规范统一（所有API以OS开头），注释详尽，结构清晰，是嵌入式RTOS入门与学习的最佳范本。

### 2. 核心局限
- **优先级唯一**：不支持同优先级多任务，一个优先级只能对应一个任务，无时间片轮转调度机制（该特性在uC/OS-III中支持）。
- **任务数量受限**：默认最多支持64个任务，最高可扩展至255个，不适合超大规模多任务场景。
- **无多核支持**：仅支持单核处理器，无法适配多核异构平台。
- **功能相对精简**：无进程、虚拟内存、文件系统、网络协议栈等高级组件，需额外搭配第三方组件实现。

## 七、源码学习建议
1.  **先搭环境跑通demo**：先在STM32等主流MCU上完成uC/OS-II的移植，创建2个基础任务，验证任务切换、延时等基础功能，建立感性认知。
2.  **吃透核心数据结构**：先深入理解OS_TCB、就绪表、OS_EVENT三大核心数据结构，这是读懂所有源码的基础。
3.  **抓住核心主线**：重点攻克“任务创建-调度器-任务切换-时钟节拍”这一核心主线，理解RTOS多任务运行的本质。
4.  **分模块逐个突破**：主线打通后，再分模块学习时间管理、内存管理、同步通信机制，逐个拆解源码逻辑。
5.  **结合调试跟踪执行**：使用仿真器单步调试，跟踪任务创建、调度、切换的完整执行流程，直观理解内核运行机制。


