分享一个小型嵌入式操作系统设计,原来精髓在这里!
内容提要
本文介绍了嵌入式操作系统的任务调度器功能,包括任务状态、就绪表、抢占式调度和时间管理。还讨论了任务的独立性、通信机制、可重入设计和互斥调用的重要性,以及任务的堆栈、数据存储区和CPU的实现方法,以及任务的挂起和恢复。
延伸解读
就绪表:位图管理任务状态
文章用32位变量OSRdyTbl表示就绪表,每位对应一个任务,0挂起、1就绪。这种位图方式节省内存,操作简单,通过宏OSSetPrioRdy和OSDelPrioRdy快速修改状态。但示例宏定义存在笔误,实际实现需按优先级置位或清零。就绪表是调度器判断任务状态的基础,优先级编号也作为任务唯一标识。
任务切换:模仿中断的现场保存
任务切换借鉴中断过程:保存当前任务的寄存器、PC、SP到其私有栈,再从待运行任务的栈恢复现场到CPU。关键是将任务私有堆栈指针赋给处理器SP。每个任务需独立堆栈存放局部变量和现场数据,任务控制块(TCB)记录栈顶指针和延时节拍数,是任务的“身份证”。
抢占式调度:优先级决定CPU归属
抢占式调度确保CPU总是执行就绪任务中优先级最高的。任务状态改变后(如主动挂起或时钟节拍中断唤醒高优先级任务),系统立即判断是否需要切换。任务级切换发生在任务主动让出CPU时,中断级切换发生在中断服务结束后。这要求每个任务有唯一优先级,且调度器能快速比较就绪任务优先级。
共享函数风险与互斥保护
多任务系统中,若低优先级任务在调用公共函数(如printf)时被高优先级任务抢占,而后者也调用同一函数,可能破坏数据。解决方法有两种:可重入设计(所有变量为局部变量,使用任务私有栈)和互斥调用(关中断、关调度、互斥信号量等)。临界资源需互斥访问,全局变量(只读除外)不能同时被多个任务访问。
Q&A
嵌入式系统中的多任务机制是如何实现的?
在单个CPU下,多任务机制本质上是任务轮流使用CPU,但由于CPU速度快且切换频繁,用户感觉多个任务同时运行。每个任务拥有独立的程序代码、堆栈和数据存储区,通过任务调度器在就绪任务中选择优先级最高的任务执行。
任务有哪几种状态?就绪表是如何表示任务状态的?
任务一般有就绪态、运行态和挂起态。就绪表用一个32位变量表示,每一位对应一个任务,0表示挂起,1表示就绪。通过宏OSSetPrioRdy和OSDelPrioRdy可以设置或清除任务的就绪状态。
什么是抢占式调度?它如何决定哪个任务运行?
抢占式调度是指一旦有更高优先级的任务就绪,就立即剥夺当前任务的CPU使用权,将CPU分配给更高优先级任务。系统为每个任务分配唯一优先级,CPU总是执行就绪任务中优先级最高的那个。
多任务系统为什么需要时钟节拍?OSTimeDly函数的作用是什么?
时钟节拍是多任务系统的“心跳”,由定时器产生固定周期中断,用于时间管理。OSTimeDly函数以时钟节拍为基准延时,它先挂起当前任务并设置延时节拍数,然后进行任务切换,当节拍数减到0时任务恢复就绪。
任务之间如何通信?为什么需要通信机制?
任务之间互相独立,不能直接调用,因此需要信号量、消息邮箱、队列等通信机制来传输信息。这些机制确保任务间能安全地交换数据或同步。
什么是可重入函数和互斥调用?它们为什么重要?
可重入函数的所有变量均为局部变量,每个任务调用时在私有栈中分配空间,互不干扰。互斥调用通过关中断、关调度、互斥信号量等方法确保临界资源一次只被一个任务访问。两者都是防止多任务环境下数据破坏的重要措施。
任务控制块(TCB)是什么?它包含哪些信息?
任务控制块是任务的“身份证”,记录任务的执行环境,如堆栈指针和延时节拍数。它把任务的程序与数据联系起来,找到TCB就能获取任务的所有资源。
如何挂起和恢复一个任务?
通过OSTaskSuspend()可以主动挂起一个任务(自身或其他任务),将其从就绪表中移出并重新调度。通过OSTaskResume()可以让被OSTaskSuspend或OSTimeDly挂起的任务恢复就绪态,然后参与调度。