嵌入式操作系统 2025 春季学期的回忆版真题。
本文连载于嵌入式操作系统-2025sp-回忆版 | HeZzz.
答案来源于 GPT-5.5 和 NotebookLM,并由我校对,可能不完全准确,仅供参考。
🙇♂️🙇♂️🙇♂️时间仓促,有不足之处烦请及时告知。邮箱hez2z@foxmail.com 或者在 速通之家 群里 @9¾。
一、简答题(5道 × 10分)
1. μC/OS-II 中的任务有哪五个状态?它们之间是如何相互转换的?
- 五个状态:睡眠状态(Dormant)、就绪状态(Ready)、运行状态(Running)、等待状态(Waiting)、中断服务状态(ISR)。(来源:课件第2章 P.8)

2. 如果系统想要取消优先级为 33 的任务,那么是怎么找到该任务在 OSRdyTbl 的具体位置?
-
μC/OS-II 优先级由 6 位二进制数决定(最高支持 64 个优先级)。优先级 转换为二进制数为 。(来源:课件第2章 P.40)
-
确定组号(行号):优先级的高 3 位表示任务所在的组号。计算算式为:
对应在就绪表中属于第 组,即映射至数组元素 OSRdyTbl[4]。(来源:课件第2章 P.40)
-
确定位号(列号):优先级的低 3 位表示任务在组内的具体位号。计算算式为:
对应在
OSRdyTbl[4]中的第 位(从第 0 位算起,屏蔽码对应 )。(来源:课件第2章 P.40) -
注销操作:若要从就绪表中清除该就绪状态,执行:
OSRdyTbl[4] &= ~0x02;(即清除第 1 位)。若清除后该元素值变为了 0,则还需要同步清除 OSRdyGrp 的第 4 位:OSRdyGrp &= ~0x10;。(来源:课件第2章 P.44)
3. 数组 OSTCBPrioTbl[] 的作用是什么?
- 作用:OSTCBPrioTbl[] 是 μC/OS-II 维护的一个指针数组,以任务优先级数字作为数组下标,对应存放该任务控制块(OS_TCB)的地址指针。(来源:课件第2章 P.31)
- 核心价值:避免了在对特定任务操作时(如挂起、恢复、修改优先级或安全删除等)需要遍历 TCB 双向链表(
OSTCBList),系统能以 的常数时间直接通过优先级找到对应的任务控制块,极大提升了实时系统查表执行的速度。(来源:课件第2章 P.31, P.32)
4. OSCtxSw() 完成的主要工作有哪些?
OSCtxSw() 为汇编编写的内核任务切换执行体,主要工作依次为:
- 将被中止(当前运行)任务的断点指针 PC 压入该任务的专用堆栈。
- 将 CPU 的所有通用寄存器(如 PSW、通用累加器、变址寄存器等)压栈保存。
- 将被中止任务当前的堆栈指针 SP 写入其控制块的
OSTCBStkPtr成员中。 - 执行 TCB 指针更新:
OSTCBCur = OSTCBHighRdy,并且OSPrioCur = OSPrioHighRdy。 - 将系统的 SP 指针重定向到新运行任务的
OSTCBStkPtr指向的地址。 - 从新任务的堆栈中弹出并恢复所有的 CPU 通用寄存器。
- 执行中断返回指令(如 RETI),从堆栈中恢复 PC 与 PSW,使 CPU 转向新任务的代码执行。(来源:课件第2章 P.57)
5. 请说出会调用 OSSched() 的 5 个系统函数(可以写中文函数名)
在 μC/OS-II 内核中,只要任务状态或优先级发生改变,系统就会调用任务级调度器 OSSched() 尝试触发抢占。会调用该函数的系统函数包括:
- 任务创建函数:
OSTaskCreate()/OSTaskCreateExt() - 延时函数:
OSTimeDly()/OSTimeDlyHMSM() - 恢复延时函数:
OSTimeDlyResume() - 挂起自身或其它任务函数:
OSTaskSuspend() - 恢复被挂起任务函数:
OSTaskResume() - 发送信号量函数:
OSSemPost() - 发送消息邮箱函数:
OSMboxPost() - 发送消息队列函数:
OSQPost()/OSQPostFront()
二、程序填空题(4个小题 × 5个空 × 1分)
1. 任务创建函数源码
INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio)
{
OS_STK *psp;
INT8U err;
#if OS_ARG_CHK_EN > 0
if (prio > OS_LOWEST_PRIO) { /* [空1] 检测优先级边界 */
return (OS_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL();
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) {
OSTCBPrioTbl[prio] = (OS_TCB *)1; /* [空2] 预留该优先级 */
OS_EXIT_CRITICAL();
psp = OSTaskStkInit(task, pdata, ptos, 0); /* [空3] 初始化任务堆栈 */
err = OSTCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0); /* [空4] 初始化任务控制块 */
if (err == OS_NO_ERR) {
if (OSRunning == TRUE) {
OS_Sched(); /* [空5] 任务级调度 */
}
} else {
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = (OS_TCB *)0;
OS_EXIT_CRITICAL();
}
return (err);
}
OS_EXIT_CRITICAL();
return (OS_PRIO_EXIST);
}
- 空1:
OS_LOWEST_PRIO(来源:课件第2章 P.16, P.20) - 空2:
OSTCBPrioTbl[prio] = (OS_TCB *)1(来源:课件第2章 P.31, P.33) - 空3:
OSTaskStkInit(task, pdata, ptos, 0)(来源:课件第2章 P.25) - 空4:
OSTCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0)(来源:课件第2章 P.33) - 空5:
OS_Sched()(或OSSched()) (来源:课件第2章 P.52, P.61)
2. OSSched() 函数源码
void OSSched (void)
{
INT8U y;
OS_ENTER_CRITICAL();
if ((OSLockNesting | OSIntNesting) == 0) { /* [空1] 确保调度未被锁定且不在中断内 */
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); /* [空2] 计算最高优先级 */
if (OSPrioHighRdy != OSPrioCur) { /* [空3] 确保最高就绪任务非当前运行任务 */
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
OSCtxSwCtr++; /* [空4] 上下文切换计数累加 */
OS_TASK_SW(); /* [空5] 执行切换宏 */
}
}
OS_EXIT_CRITICAL();
}
- 空1:
(OSLockNesting | OSIntNesting) == 0(来源:课件第2章 P.52) - 空2:
OSUnMapTbl[OSRdyTbl[y]](来源:课件第2章 P.52) - 空3:
OSPrioCur(来源:课件第2章 P.52) - 空4:
OSCtxSwCtr(来源:课件第2章 P.52) - 空5:
OS_TASK_SW()(来源:课件第2章 P.52)
3. OSTimeTick() 源码
void OSTimeTick (void)
{
OS_TCB *ptcb;
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
OS_ENTER_CRITICAL();
OSTime++; /* [空1] 递增系统节拍计数 */
OS_EXIT_CRITICAL();
ptcb = OSTCBList; /* [空2] 获取活动任务 TCB 链表表头 */
while (ptcb->OSTCBPrio < OS_LOWEST_PRIO) {
OS_ENTER_CRITICAL();
if (ptcb->OSTCBDly != 0) {
if (--ptcb->OSTCBDly == 0) {
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* [空3] 确保任务未被强制挂起 */
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; /* [空4] 将任务状态改写为就绪 */
} else {
ptcb->OSTCBDly = 1;
}
}
}
ptcb = ptcb->OSTCBNext; /* [空5] 指向链表的下一个任务 TCB */
OS_EXIT_CRITICAL();
}
}
- 空1:
OSTime(来源:课件第3章 P.16) - 空2:
OSTCBList(来源:课件第2章 P.32) - 空3:
OS_STAT_SUSPEND(来源:课件第2章 P.29) - 空4:
ptcb->OSTCBBitX(来源:课件第2章 P.28) - 空5:
ptcb->OSTCBNext(来源:课件第2章 P.28, P.32)
4. OSSemaphore() 信号量创建源码
OS_EVENT *OSSemCreate (INT16U cnt)
{
OS_EVENT *pevent;
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
if (OSIntNesting > 0) {
return ((OS_EVENT *)0);
}
OS_ENTER_CRITICAL();
pevent = OSEventFreeList; /* [空1] 从空闲 ECB 链表中取一个 */
if (OSEventFreeList != (OS_EVENT *)0) {
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; /* [空2] 调整空闲链表头 */
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) {
pevent->OSEventType = OS_EVENT_TYPE_SEM; /* [空3] 设置事件类型为信号量 */
pevent->OSEventCnt = cnt; /* [空4] 写入信号量计数初值 */
pevent->OSEventPtr = (void *)0;
OS_EventWaitListInit(pevent); /* [空5] 初始化等待链表 */
}
return (pevent);
}
- 空1:
OSEventFreeList(来源:课件第4章 P.23, P.29) - 空2:
OSEventPtr(来源:课件第4章 P.23, P.29) - 空3:
OS_EVENT_TYPE_SEM(注:课件第4章 P.29 源码图中印为 OS_ENVENT_TYPE_SEM) (来源:课件第4章 P.14, P.29) - 空4:
cnt(来源:课件第4章 P.29) - 空5:
OS_EventWaitListInit(来源:课件第4章 P.18, P.29)
三、综合题(10 + 20 分)
1. 简述 OSUnMapTbl[] 的赋值原理(10分)
OSUnMapTbl[](优先级判定查找表)是大小为 256 的数组。其核心设计目的是为了通过查表法,在常数时间 内直接检索出 8 位无符号整数中“处于最低有效位(最右侧)的 1 的位数”,从而极速判断出当前系统中就绪的最高任务优先级。(来源:课件第2章 P.45, P.46, P.48)
- 下标输入与位代表含义:数组下标值 () 代表当前字节位状态。该字节的 八个位分别代表组号(或位号)的就绪状态(
1为就绪,0为未就绪)。按照 μC/OS-II 规则,越低位的二进制位代表的优先级数字越小、优先级越高。因此寻找最高就绪任务,本质上就是寻找下标 在二进制状态下从右向左数出现的第一个1。(来源:课件第2章 P.38, P.48) - 判定逻辑与赋值原理:
-
D0 位为 1:如果 (即 为奇数,),则无论其他更高位状态如何,第 0 位对应的任务都是就绪且优先级最高的,因此该类下标对应的值均为
0。 -
D1 位为 1(D0为0):若 且 (即 为偶数且能被 2 整除,但不能被 4 整除,符合 且 ),第 1 位对应的任务是就绪中最高级的,对应的数组元素值均为
1。 -
D2 位为 1(D0、D1为0):若 , 且 (即 能被 4 整除,但不能被 8 整除),最高级在第 2 位,数组元素值均为
2。 -
以此类推:通过将 8 位二进制数的各种组合(0~255)按右起第一个 1 的位置划分,分别填入
0、1、2、3、4、5、6、7。对于 ,由于没有任何任务就绪,为了代码安全默认其数组值OSUnMapTbl[0] = 0。(来源:课件第2章 P.48, P.49)
-
2. 时间轴分析与输出预测(20分)
/************************Test*************************************/
#include "includes.h"
#define TASK_STK_SIZE 512 // 任务堆栈长度
OS_STK StartTaskStk[TASK_STK_SIZE]; // 定义任务堆栈区
OS_STK MyTaskStk[TASK_STK_SIZE]; // 定义任务堆栈区
OS_STK YouTaskStk[TASK_STK_SIZE]; // 定义任务堆栈区
OS_STK HerTaskStk[TASK_STK_SIZE]; // 定义任务堆栈区
INT16S key; // 用于退出的键
char *s1 = "MyTask正在运行";
char *s2 = "YouTask正在运行";
char *s3 = "HerTask正在运行";
INT8U err;
INT8U y = 0; // 字符显示位置
OS_FLAG_GRP *Sem_F;
OS_FLAGS Flags;
void StartTask(void *data); // 声明起始任务
void MyTask(void *data); // 声明任务
void YouTask(void *data); // 声明任务
void HerTask(void *data); // 声明任务
/************************主函数*********************************/
void main(void)
{
OSInit(); // 初始化 uC/OS-II
PC_DOSSaveReturn(); // 保存 DOS 环境
PC_VectSet(uCOS, OSCtxSw); // 安装 uC/OS-II 中断
Sem_F = OSFlagCreate(0, &err); // 创建信号量集
OSTaskCreate(StartTask, // 创建任务 StartTask
(void *)0, // 给任务传递参数
&StartTaskStk[TASK_STK_SIZE - 1], // 设置任务堆栈栈顶
0); // 使任务的优先级别为 0
OSStart(); // 启动多任务管理
}
/***********************任务 StartTask*******************************/
void StartTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
pdata = pdata;
OS_ENTER_CRITICAL();
PC_VectSet(0x08, OSTickISR); // 安装时钟中断向量
PC_SetTickRate(OS_TICKS_PER_SEC); // 设置 uC/OS-II 时钟频率
OS_EXIT_CRITICAL();
OSStatInit(); // 初始化统计任务
OSTaskCreate(MyTask, // 创建任务 MyTask
(void *)0, // 给任务传递参数
&MyTaskStk[TASK_STK_SIZE - 1], // 设置任务堆栈栈顶
3); // 使任务的优先级别为 3
OSTaskCreate(YouTask, // 创建任务 YouTask
(void *)0, // 给任务传递参数
&YouTaskStk[TASK_STK_SIZE - 1], // 设置任务堆栈栈顶
4); // 使任务的优先级别为 4
OSTaskCreate(HerTask, // 创建任务 HerTask
(void *)0, // 给任务传递参数
&HerTaskStk[TASK_STK_SIZE - 1], // 设置任务堆栈栈顶
5); // 使任务的优先级别为 5
for (;;)
{
// 如果按下 Esc 键则退出 uC/OS-II
if (PC_GetKey(&key) == TRUE)
{
if (key == 0x1B)
{
PC_DOSReturn();
}
}
OSTimeDlyHMSM(0, 0, 3, 0); // 等待 3 秒
}
}
/************************任务 MyTask*******************************/
void MyTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
pdata = pdata;
for (;;)
{
Flags = OSFlagQuery( // 请求信号量集
Sem_F,
//(OS_FLAGS)3, // 请求第 0 位和第 1 位信号
//OS_FLAG_WAIT_SET_ALL, 0, // 第 0 位和第 1 位信号都为 1 为有效
&err
);
switch (Flags)
{
case 1:
s1 = "第0位信号有效";
PC_DispStr(10, ++y, s1, DISP_BGND_BLACK + DISP_FGND_WHITE);
break;
case 2:
s1 = "第1位信号有效";
PC_DispStr(10, ++y, s1, DISP_BGND_BLACK + DISP_FGND_WHITE);
break;
case 3:
s1 = "第0和第1位信号都有效";
PC_DispStr(10, ++y, s1, DISP_BGND_BLACK + DISP_FGND_WHITE);
break;
}
OSFlagPost( // 发送信号量集
Sem_F,
(OS_FLAGS)3, // 给第 0、1 位发信号
OS_FLAG_CLR, // 信号置 0
&err
);
OSTimeDlyHMSM(0, 0, 2, 0); // 等待 2 秒
}
}
/************************任务 YouTask******************************/
void YouTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
pdata = pdata;
for (;;)
{
PC_DispStr(10, ++y, s2, DISP_BGND_BLACK + DISP_FGND_WHITE);
OSTimeDlyHMSM(0, 0, 4, 0); // 延时 4 秒
OSFlagPost( // 发送信号量集
Sem_F,
(OS_FLAGS)2, // 给第 1 位发信号
OS_FLAG_SET, // 信号置 1
&err
);
OSTimeDlyHMSM(0, 0, 2, 0); // 等待 2 秒
}
}
/************************任务 HerTask******************************/
void HerTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
pdata = pdata;
for (;;)
{
PC_DispStr(10, ++y, s3, DISP_BGND_BLACK + DISP_FGND_WHITE);
if (y < 10)
{
OSFlagPost( // 发送信号量集
Sem_F,
(OS_FLAGS)1, // 给第 0 位发信号
OS_FLAG_SET, // 信号置 1
&err
);
}
OSTimeDlyHMSM(0, 0, 1, 0); // 等待 1 秒
}
}
/************************End**************************************/
(1) 系统任务优先级关系
StartTask(系统起步任务):优先级为 0(最高级)MyTask:优先级为 3YouTask:优先级为 4HerTask:优先级为 5(最低级)
(2) 任务运行规律分析(以 为系统时间轴,单位:秒/s)
-
s 起步时刻:
StartTask优先级最高,率先获得 CPU 运行权。它完成时钟安装并创建MyTask(Prio 3)、YouTask(Prio 4) 以及HerTask(Prio 5)。StartTask运行结束前调用OSTimeDlyHMSM(0, 0, 3, 0)(延时 3 秒,将在 唤醒并就绪)。- 由于
StartTask进入等待,系统触发调度。就绪任务中最高级为MyTask(Prio 3)。 MyTask开始执行。调用OSFlagQuery查询信号量集Sem_F的值(初始值为 0)。Flags返回0。switch(Flags)中没有匹配的分支,不执行任何打印动作。MyTask向Sem_F发送置 0 清除操作OSFlagPost(Sem_F, 3, OS_FLAG_CLR)(此时标志已经是 0),接着调用OSTimeDlyHMSM(0, 0, 2, 0)延时 2 秒,主动释放 CPU(将在 唤醒)。- 系统再次调度,执行次高级任务
YouTask(Prio 4)。 YouTask在屏幕第++y(此时 自增为 1)行打印:“YouTask正在运行” (Line 1)。YouTask调用OSTimeDlyHMSM(0, 0, 4, 0)延时 4 秒(将在 唤醒并执行延时后的下一语句)。- 系统再次调度,执行最低级就绪任务
HerTask(Prio 5)。 HerTask在屏幕第++y(此时 自增为 2)行打印:“HerTask正在运行” (Line 2)。HerTask检测此时 ,调用OSFlagPost发送置 1 信号:给Sem_F的第 0 位置 1(标志组状态变更为0x01)。HerTask调用OSTimeDlyHMSM(0, 0, 1, 0)延时 1 秒释放 CPU(将在 唤醒)。- 此时已无就绪任务,系统运行空闲任务
OSTaskIdle。
-
s:
HerTask(Prio 5) 延时到期唤醒。此时其优先级最高,开始执行。HerTask打印:“HerTask正在运行” (Line 3, y自增为3)。- 检测 ,给
Sem_F第 0 位置 1(当前标志状态依然为0x01)。 - 调用延时 1 秒主动释放 CPU,将在 唤醒。
-
s:
MyTask(Prio 3) 和HerTask(Prio 5) 的延时均到期唤醒。- 由于
MyTask优先级更高,优先执行。 MyTask调用OSFlagQuery查询到信号量集为0x01(第 0 位为 1),匹配case 1分支。- 打印:“第0位信号有效” (Line 4, y自增为4)。
- 调用
OSFlagPost强制将Sem_F的第 0、1 位清零(当前标志组重置为0x00)。 - 调用延时 2 秒释放 CPU,将在 唤醒。
- 次优先级的
HerTask(Prio 5) 开始执行,打印:“HerTask正在运行” (Line 5, y自增为5)。 - 检测 ,再次向
Sem_F第 0 位置 1(标志组状态更新为0x01)。 - 调用延时 1 秒释放 CPU,将在 唤醒。
-
s:
StartTask(Prio 0) 和HerTask(Prio 5) 的延时均到期唤醒。- 高优先级的
StartTask优先执行。检测键盘(未按 Esc),继续延时 3 秒(将在 唤醒)。 HerTask(Prio 5) 执行,打印:“HerTask正在运行” (Line 6, y自增为6)。- 检测 ,再次向
Sem_F第 0 位置 1(标志状态维持为0x01)。 - 调用延时 1 秒,将在 唤醒。
-
s:
MyTask(Prio 3)、YouTask(Prio 4) 以及HerTask(Prio 5) 的延时均到期唤醒。MyTask(Prio 3) 最优执行:- 查询到标志为
0x01(第 0 位有效),进入case 1。 - 打印:“第0位信号有效” (Line 7, y自增为7)。
- 将标志位重新清零。
- 进入延时 2 秒状态(将在 唤醒)。
- 查询到标志为
YouTask(Prio 4) 开始运行(完成先前的 4s 延时,执行下一行):- 向
Sem_F的第 1 位置 1:OSFlagPost(Sem_F, 2, OS_FLAG_SET)(由于刚才已被MyTask清零,当前标志组变为0x02)。 - 进入延时 2 秒状态(将在 唤醒)。
- 向
HerTask(Prio 5) 开始运行:- 打印:“HerTask正在运行” (Line 8, y自增为8)。
- 检测 ,调用
OSFlagPost给第 0 位置 1。 - 此时标志组状态变更为:
0x02 | 0x01 = 0x03(第 0、1 位同时有效)。 - 进入延时 1 秒状态(将在 唤醒)。
-
s:
HerTask(Prio 5) 唤醒并执行。- 打印:“HerTask正在运行” (Line 9, y自增为9)。
- 检测 ,再次向第 0 位发送置 1 信号(标志状态维持为
0x03)。 - 进入延时 1 秒状态(将在 唤醒)。
-
s:
StartTask(Prio 0)、MyTask(Prio 3)、YouTask(Prio 4)、HerTask(Prio 5) 全部到期唤醒。- 最优先的
StartTask(Prio 0) 先执行,无按键,继续调用延时 3 秒(将在 唤醒)。 - 次优先的
MyTask(Prio 3) 开始执行:- 查询信号量集得到
0x03(两标志皆有效),成功命中case 3逻辑分支。 - 设置
s1字符串并打印:“第0和第1位信号都有效” (Line 10, y自增为10)。 - 发送置 0 清除操作。
- 进入延时 2 秒状态。
- 查询信号量集得到
(3) 屏幕前 10 行打印输出预测
根据以上时间轴及变量 的递增过程,屏幕最终输出的前 10 行结果严格为:
- 第 1 行:
YouTask正在运行 - 第 2 行:
HerTask正在运行 - 第 3 行:
HerTask正在运行 - 第 4 行:
第0位信号有效 - 第 5 行:
HerTask正在运行 - 第 6 行:
HerTask正在运行 - 第 7 行:
第0位信号有效 - 第 8 行:
HerTask正在运行 - 第 9 行:
HerTask正在运行 - 第 10 行:
第0和第1位信号都有效