04 μC/OS-II 操作系统
考频:★★★★★(高频,选择题 + 填空题 + 综合题均有)
一、μC/OS-II 基本特点
考频:★★★★★(特点描述题必考)
| 特点 | 具体内容 | 易错点 |
|---|---|---|
| 内核类型 | 抢占式实时操作系统内核 | 是抢占式,不是协作式 |
| 最多任务数 | 64个(总计) | 不是 63 个! |
| 内核保留 | 4个(空闲任务、统计任务等) | |
| 用户可用任务 | 60个 | 最常考错:不是 63 个! |
| 任务优先级 | 0~63,0 最高 | 数字越小,优先级越高 |
| 优先级唯一性 | 每个任务有唯一的优先级 | 不允许两个任务有相同优先级 |
| 时间片 | 不支持时间片轮转 | 纯优先级调度,不用时间片 |
| 栈空间 | 允许每个任务有不同的栈大小 | |
| 可移植性 | C 语言编写,少量汇编,可移植 |
[!WARNING] 两个超级易错点:
用户可用任务是 60 个,不是 63 个!(64 - 4 = 60,内核占用 4 个)
μC/OS-II 不支持时间片轮转! 它是纯优先级抢占调度,高优先级任务一旦就绪就会立即抢占 CPU。
二、任务的五种状态
考频:★★★★(状态转换图必考)
五种状态
| 状态 | 英文 | 说明 |
|---|---|---|
| 休眠态(Dormant) | Dormant | 任务代码已存在,但未被 OSTaskCreate() 创建或已被删除,不参与调度 |
| 就绪态(Ready) | Ready | 任务已创建,等待 CPU 使用权 |
| 运行态(Running) | Running | 任务正在 CPU 上执行 |
| 等待态(Waiting/Blocked) | Waiting | 任务在等待某个事件/资源/时间(如等信号量、等延时到期) |
| 被中断态(Interrupted) | Interrupted | 任务执行被中断服务程序打断 |
状态转换图
OSTaskCreate()
休眠态 ─────────────────────→ 就绪态
↑ ↕ ← 调度器选中/被更高优先级任务抢占
│ 运行态
│ OSTaskDel() ↓ ← 等待事件/资源(如 OSTimeDly())
│ 等待态
│ ↓ 等待的事件发生 → 回到就绪态
│ 中断发生
└──────── 被中断态 ←───────────
[!CAUTION] 重要区分:
OSTimeDly() 调用后,任务进入的是"等待态(Waiting)",不是"被中断态"!
被中断态是由硬件中断引起的,而 OSTimeDly() 是任务主动放弃 CPU 去延时等待,进入等待态。
三、任务管理关键函数
考频:★★★★★(综合题必考)
核心函数一览
| 函数 | 作用 | 备注 |
|---|---|---|
OSInit() |
初始化 μC/OS-II 系统 | 最先调用,必须在所有其他 μC/OS-II 函数之前 |
OSTaskCreate() |
创建一个任务 | 综合题"第19空"常考 |
OSStart() |
启动多任务调度 | 综合题"最后一步"必考,调用后系统进入调度循环 |
OSTimeDly(n) |
任务延时 n 个时钟节拍 | 调用后任务进入等待态 |
OS_ENTER_CRITICAL() |
关中断,进入临界区 | 保护不可被打断的代码段 |
OS_EXIT_CRITICAL() |
开中断,退出临界区 | 与 ENTER 成对使用 |
OSSchedLock() |
锁定调度器(禁止任务切换) | 不关中断,但禁止调度 |
OSSchedUnlock() |
解锁调度器 | 与 Lock 成对使用 |
函数详解
OSTaskCreate() 原型
INT8U OSTaskCreate(
void (*task)(void *pd), // 任务函数指针
void *pdata, // 传给任务的参数
OS_STK *ptos, // 任务栈顶指针
INT8U prio // 任务优先级(0~63)
);
OSTimeDly() 说明
四、任务间通信机制
考频:★★★(选择题)
μC/OS-II 提供以下几种任务间通信/同步机制:
| 机制 | 作用 |
|---|---|
| 信号量(Semaphore) | 同步控制,控制共享资源的访问 |
| 互斥信号量(Mutex) | 互斥访问,防止优先级反转 |
| 邮箱(Mailbox) | 传递一个消息(一个指针) |
| 消息队列(Message Queue) | 传递多个消息(队列形式) |
| 事件标志组(Event Flag) | 多个事件的组合等待 |
[!WARNING] "内存文件"不是 μC/OS-II 的通信机制!
考题中如果问"μC/OS-II 任务间进行大规模数据共享用哪种方式",选项里出现"内存文件",这是错误选项(选 D 是陷阱)。
μC/OS-II 没有"内存文件"这个概念,大量数据共享通常用全局变量+信号量保护,或消息队列传递指针。
五、时钟节拍(Tick)
考频:★★★★(时序关系必考)
时钟节拍是什么?
时钟节拍是 μC/OS-II 的"心跳",由硬件定时器周期性产生中断,驱动系统进行任务调度、延时计数等。
关键规则
| 规则 | 说明 |
|---|---|
| 节拍率越高,系统负荷越重 | 中断太频繁,CPU 大量时间用在中断服务上 |
| 在 OSStart() 之后激活时钟节拍 | 这是正确顺序! |
[!CAUTION] 高频易错点:时钟节拍的激活时机!
正确做法:先调用
OSStart()启动多任务,然后在某个任务里(通常是最高优先级的启动任务)初始化并激活定时器中断。错误做法:在调用
OSStart()之前就激活时钟节拍中断——这是错误的,可能导致系统崩溃(因为 μC/OS-II 的数据结构还没完全准备好,却已经开始节拍中断)。
六、综合题编程模板
考频:★★★★★(综合题必考,19、20空必须记住)
标准程序框架
#include "includes.h"
/* 任务栈 */
OS_STK TaskAStk[TASK_STK_SIZE];
OS_STK TaskBStk[TASK_STK_SIZE];
/* 任务函数声明 */
void TaskA(void *pdata);
void TaskB(void *pdata);
int main(void) {
/* 第一步:初始化 μC/OS-II */
OSInit();
/* 第二步:创建任务(综合题"第19空"填 OSTaskCreate)*/
OSTaskCreate(TaskA, (void *)0, &TaskAStk[TASK_STK_SIZE-1], 10);
OSTaskCreate(TaskB, (void *)0, &TaskBStk[TASK_STK_SIZE-1], 11);
/* 第三步:启动多任务调度(综合题"第20空"填 OSStart)*/
OSStart(); // ← 这行之后程序永不返回,OS 开始调度
return 0; // 实际上永远不会执行到这里
}
void TaskA(void *pdata) {
/* 在任务里初始化时钟节拍(在 OSStart 之后才对) */
InitTimerTick(); // 初始化硬件定时器,激活时钟节拍
while (1) {
/* 任务主体代码 */
DoSomething();
OSTimeDly(100); // 延时 100 个节拍,进入等待态
}
}
综合题填空要点
| 位置 | 答案 | 说明 |
|---|---|---|
| 第 19 空 | OSTaskCreate |
创建任务的函数 |
| 第 20 空 | OSStart |
启动多任务调度 |
[!TIP] 记忆顺序:
OSInit()→OSTaskCreate()→OSStart()这三步是 μC/OS-II 程序的固定套路,一步都不能少,顺序也不能错。
本章小结
μC/OS-II 核心数字:
总任务 64 个 → 内核占 4 个 → 用户可用 60 个(不是63!)
优先级 0~63,0 最高
五种状态:休眠 → 就绪 → 运行 → 等待 → 被中断
关键函数顺序:OSInit() → OSTaskCreate() → OSStart()
OSTimeDly() 后进"等待态"(不是被中断态!)
不支持时间片轮转!(纯优先级抢占)
时钟节拍在 OSStart() 之后激活!
"内存文件"不是 μC/OS-II 的通信机制!
真题演练
以下是NCRE三级嵌入式真题中与本节相关的典型考题:
1. uC/OS-II操作系统启动的过程中,Bootloader执行完毕后,调用应用程序主文件里的main()函数。main()函数在执行过程中,除了用户函数和硬件初始化函数之外,需要执行以下三个主要的uC/OS-II函数:I. OSStart() II. OSTaskCreate() III. OSInit()。它们的执行顺序是( )。
- A)I、II、III
- B)III、I、II
- C)II、III、I
- D)III、II、I
答案解析
答:D main()函数中需要按顺序执行以下三个主要的uC/OS-II函数:OSInit()(操作系统初始化)-> OSTaskCreate()(创建任务)-> OSStart()(启动多任务调度)。一旦OSStart()函数开始执行,就标志着uC/OS-II进入了多任务调度的正常运行状态。
2. 在uC/OS-II中,OSSched()函数是任务调度的前导函数,用于判断进行任务调度的条件是否满足。下列满足条件的变量值为( )。
- A)OSIntNesting=0,OSLockNesting=0
- B)OSIntNesting=0,OSLockNesting=1
- C)OSIntNesting=1,OSLockNesting=0
- D)OSIntNesting=1,OSLockNesting=1
答案解析
答:A OSSched()判断进行任务调度的三个条件:(1) OSIntNesting=0,即所有的中断服务程序已执行完毕;(2) OSLockNesting=0,即调度没有被禁止(调度器未上锁);(3) 就绪表中最高优先级任务的优先级比当前任务的优先级高。前两个条件都必须为0。
3. 在uC/OS-II中,OSInit()函数先建立最初的任务就绪表,然后建立4个空白的数据链表。这4个空白的数据链表是( )。
- A)任务控制块链表、事件控制块链表、标志链表和进程控制块链表
- B)任务控制块链表、事件控制块链表、消息链表和进程控制块链表
- C)任务控制块链表、事件控制块链表、标志链表和内存控制块链表
- D)任务控制块链表、事件控制块链表、消息链表和内存控制块链表
答案解析
答:C OSInit()函数先建立最初的任务就绪表,然后建立4个空白的数据链表:任务控制块链表、事件控制块链表、标志链表和内存控制块链表。注意没有"进程控制块链表"和"消息链表"这样的说法。
4. 【填空题】uC/OS-II的每个任务都处在休眠态、就绪态、_态、挂起态和被中断态5种状态之一的状态下。其中,在就绪态中uC/OS-II就绪表为每一个任务提供一个二进制位,登记该任务是否就绪,就绪时对应位的值为_。
答案
答:运行;1 uC/OS-II的每个任务都处在休眠态、就绪态、运行态、挂起态(等待某一事件发生)和被中断态5种状态之一。就绪表中每个任务对应一个位元,就绪时取值为1,没有就绪时取值为0。