跳转至

04 μC/OS-II 操作系统

考频:★★★★★(高频,选择题 + 填空题 + 综合题均有)


一、μC/OS-II 基本特点

考频:★★★★★(特点描述题必考)

特点 具体内容 易错点
内核类型 抢占式实时操作系统内核 是抢占式,不是协作式
最多任务数 64个(总计) 不是 63 个!
内核保留 4个(空闲任务、统计任务等)
用户可用任务 60个 最常考错:不是 63 个!
任务优先级 0~63,0 最高 数字越小,优先级越高
优先级唯一性 每个任务有唯一的优先级 不允许两个任务有相同优先级
时间片 不支持时间片轮转 纯优先级调度,不用时间片
栈空间 允许每个任务有不同的栈大小
可移植性 C 语言编写,少量汇编,可移植

[!WARNING] 两个超级易错点:

  1. 用户可用任务是 60 个,不是 63 个!(64 - 4 = 60,内核占用 4 个)

  2. μ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() 说明

OSTimeDly(100);   // 延时 100 个时钟节拍
// 调用后:任务从运行态 → 等待态
// 等待 100 个节拍后,自动回到就绪态

四、任务间通信机制

考频:★★★(选择题)

μ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。