🔥作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生,研究方向无线联邦学习 🎬擅长领域:驱动开发,嵌入式软件开发,BSP开发 ❄️作者主页:一个平凡而乐于分享的小比特的个人主页 ✨收录专栏:UCOS-III,本专栏为UCOS-III学习记录 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖
我们来一起深入浅出地剖析一下 uC/OS-III 内核的“心脏”之一——就绪列表。
你可以把整个 uC/OS-III 想象成一个高效的公司,而就绪列表就是公司的“任务调度中心”。这个调度中心的核心工作就是:快速找出当前最重要、最应该被执行的“员工”(任务),然后把CPU资源分配给它。
通俗理解: 就绪列表是一个数据结构,它用来记录和管理系统中所有已经准备就绪、随时可以运行的任务。当一个任务等待的事件(比如延时、信号量、消息等)发生时,它就会被放入就绪列表。反之,当它开始等待某个事件时,就会被移出就绪列表。
核心目标: 在常数时间 O(1) 内,找出当前优先级最高的就绪任务。 这是实时操作系统的生命线。
uC/OS-III 的就绪列表的实现非常精巧,它不像一个简单的待办事项清单,而更像一个配备了高效索引系统和分组管理机制的智能任务池。它由三个核心部分构成:
OSRdyMap) - “部门总管”OSRdyList) - “部门员工花名册”OSRdyTbl[]) - “部门分组清单”下面我们通过一个公司的场景来详细解释这三者如何协同工作。
假设我们的公司(uC/OS-III内核)有 64 个优先级(0-63,优先级0最高,63最低),就像有64个不同等级的职位。每个优先级上只能有一个任务(一个员工)。
OSRdyMap (部门总管)我们把 64 个优先级分成 8 个组(因为 OSRdyMap 是 8 位),每组 8 个优先级。
OSRdyMap 的位 | 代表的优先级组 |
|---|---|
位 0 | 优先级 0 ~ 7 (最高优先级组) |
位 1 | 优先级 8 ~ 15 |
位 2 | 优先级 16 ~ 23 |
… | … |
位 7 | 优先级 56 ~ 63 (最低优先级组) |
工作方式:
OSRdyMap 中对应的位就被置为 1。举例:
如果 OSRdyMap = 0b00000101(二进制),这意味着:
总管的工作:调度器一来,总管 (OSRdyMap) 看一眼就知道该去哪个组(比如 0~7 组)找最高优先级的任务,因为它从位 0 开始找,找到的第一个 ‘1’ 就是最高优先级的组。
OSRdyTbl[] (部门分组清单)OSRdyTbl[8](对应 8 个组)。每个数组元素 (OSRdyTbl[i]) 对应 OSRdyMap 的一个位,管理 8 个优先级。
数组索引 i | 管理的优先级范围 | 对应 OSRdyMap 的位 |
|---|---|---|
OSRdyTbl[0] | 0 ~ 7 | 位 0 |
OSRdyTbl[1] | 8 ~ 15 | 位 1 |
… | … | … |
OSRdyTbl[7] | 56 ~ 63 | 位 7 |
工作方式:
OSRdyTbl[i] 中的每一位代表该组内的一个优先级。
OSRdyTbl[0] 的位 0 对应优先级 0)。OSRdyTbl[0] 的位 7 对应优先级 7)。OSRdyTbl[i] 中对应的位就被置 1。接上例:
OSRdyMap = 0b00000101,我们知道 0~7 组和 16~23 组有任务。
OSRdyTbl[0] (管理0~7组),假设其值为 0b00100010。这意味着: OSRdyTbl[2] (管理16~23组),假设其值为 0b10000000。这意味着: 清单的工作:现在调度器知道了,在最高优先级的组(0~7)里,具体是优先级1和优先级5上有任务。
OSRdyList[] (部门员工花名册)OSRdyList[64]。结构体成员通常包括:
HeadPtr:指向该优先级就绪链表头任务的指针。TailPtr:指向该优先级就绪链表尾任务的指针。NbrEntries:该链表中当前有多少个任务。工作方式:
OSRdyList 链表中。接上例:
OSRdyList[1] 这个链表里,可能只有一个任务(比如 Task_A)。OSRdyList[5] 这个链表里,可能有三个任务(Task_B, Task_C, Task_D),它们正在以时间片轮转的方式共享 CPU。OS_CFG_MAX_PRIO-1:最低优先级(通常是空闲任务)os_cfg.h 中通过 OS_CFG_MAX_PRIO 进行配置。通常可以是 8, 16, 32, 64, … 最高可达 256 级(但常用 64 级)。OS_CFG_MAX_PRIO-1 通常被系统占用: OS_IntQTask),用于处理中断延迟发布,它拥有至高无上的权力。OS_IdleTask),当所有其他任务都无事可做时,它来“摸鱼”,消耗 CPU 时间。下图展示了 uC/OS-III 中典型的优先级布局:

现在,让我们把“调度中心”的整个工作流程串起来,看看当调度器需要寻找最高优先级任务时发生了什么:

举例:寻找最高优先级任务
OSRdyMap): 0b00000101OSRdyTbl[0]): 0b00100010P = 0 * 8 + 1 = 1。所以,当前最高就绪优先级是 1。OSRdyList[1]): OSRdyList[1].HeadPtr 所指向的任务控制块 (TCB)。整个过程通过查表和一两条位运算指令完成,速度极快,是严格的 O(1) 时间复杂度!
为了让理解更深刻,我们来看两个核心操作:
假设任务 Task_Player (优先级 20) 的延时时间到了。
Y = 20 / 8 = 2X = 20 % 8 = 4OSRdyTbl[2] 的第 4 位置 1。OSRdyTbl[2] 从 0 变成了非 0,所以将 OSRdyMap 的第 2 位置 1。Task_Player 的 TCB 插入到 OSRdyList[20] 链表的尾部。假设任务 Task_Network (优先级 15) 请求一个信号量,但信号量不可用,任务需要挂起等待。
Y = 15 / 8 = 1X = 15 % 8 = 7Task_Network 的 TCB 从 OSRdyList[15] 链表中移除。OSRdyList[15] 链表现在为空(NbrEntries == 0),则将 OSRdyTbl[1] 的第 7 位置 0。OSRdyTbl[1] 现在变成了 0(整个组都没有就绪任务了),则将 OSRdyMap 的第 1 位置 0。组件 | 比喻 | 数据类型 | 作用 | 关键信息 |
|---|---|---|---|---|
OSRdyMap | 部门总管 | 整数 (e.g., uint8_t) | 宏观索引,快速定位哪个优先级分组有任务 | “哪些组忙?” |
OSRdyTbl[] | 部门分组清单 | 整数数组 (e.g., uint8_t[8]) | 中观索引,定位组内哪个优先级有任务 | “组里哪些职位有人?” |
OSRdyList[] | 部门员工花名册 | 结构体数组 (e.g., OS_RDY_LIST[64]) | 微观管理,管理同一优先级下的所有任务链表 | “这个职位上具体是谁在待命?” |
三者关系总结:
OSRdyMap 和 OSRdyTbl[] 共同构成了一个两级位图索引系统,用于在常数时间内锁定最高就绪优先级。OSRdyList[] 则是在确定优先级后,管理该优先级下多个任务的详细清单,为时间片轮转调度提供了基础设施。通过这种精妙的分层设计,uC/OS-III 的就绪列表完美地平衡了调度速度和功能灵活性,成为其稳定高效运行的坚实基石。