实时操作系统内核的任务调度点

嵌入式系统软件工程师需要对实时系统的方方面面有足够的理解才能很好的应对各种可能出现的问题,以及新的需求。本文对基于优先级调度的抢占式实时操作系统的任务调度点进行了小结,为什么要知道任务调度的时机呢?楼主目前想到了以下的一些理由:

1 加深对于RTOS的理解
2 为了做一个任务运行profile的分析工具,即可以看出某时间点之前一段时间内的哪些任务运行了,各自又运行了多长时间
3 便于定位一些莫名其妙的问题;例如应用工程师测试某些较为复杂case的时候,频繁报出一个类似的内存被改写的错误,如果牵扯到的task和代码流程很多,这种问题只看现象分析往往很难定位。这个时候就需要在任务调度点加上一些调试代码,如果某块内存被不符合预期得改写,就立刻死机并查看现场,找出最后一次执行的task,并回溯其调用栈以及局部、全局变量,很快就能找出原因。

有了目标,学习就有了动力。那么基于优先级调度的抢占式实时操作系统有哪些可能的调度点呢?
1 初始化线程结束的时候。在初始化线程(例如main函数),要求创建至少一个task,因为一般在初始化线程的结尾处,会开启任务的调度,往往这个OS_Start_scheduler函数是不会返回的,它要做的是开启system tick中断,找出第一个、优先级最高的task,跳到它的函数入口处开始执行。这是任务调度的开始点。
2 任务被创建的时候。任务允许其它任意的task执行函数里被创建,新建的task优先级可能会高于当前运行的task,这种情况下,新创建的task会立刻抢占当前task,执行一次调度,获得CPU开始执行。备注:有的RTOS里,OS_Create_Task和OS_Start_Task是两个单独的API,这样的话,需要先Create再Start一下才会有这种效果。
3 类似的,任务被删除的时候。如果当前执行的任务调用内核提供的API如OS_Delete_Task,删除了自己,它会将当前task从就绪列表中移除,并释放其数据结构所占内存。那么显然地,必须要有一个其它task来接力,这时候需要执行一次调度,找到下一个优先级最高的就绪态任务,给其分配CPU
4 任务调用内核提供的API来主动放弃CPU,例如OS_Task_Suspend,将当前执行的任务ID作为入参传入,该函数不会立刻返回,而是在函数内部,启用一次任务调度,找到下一个执行的task并执行。请注意:一般不建议应用程序员直接调用该API,原因很简单,因为你一旦suspend住了自己,只能寄希望与其它task来重新将你的task唤醒(resume),这会大大增加多个task间的耦合以及时序依赖关系,是非常不好的设计。如果要主动让出CPU,应该使用下面两条提供的办法。
5 将任务睡眠,API的名字类似于OS_Task_Sleep,它可以接收一个以时间或者system tick的周期倍数作为入参。它的实现一般就是将该task从就绪态链表上摘下,挂到睡眠(或者也叫挂起、阻塞等)态的任务链表上,同时启动一个内核软件定时器,在该时间过后由触发内核将本task放到就绪态链表上,竞争CPU。之后就触发一次任务调度,寻找下一个应该允许的task。关于任务睡眠更多内容,可以参考 实时操作系统的任务睡眠
6 OS_Wait_For_Message,这是一种很常用的方法,大部分的task执行函数的主体结构都如下面所示:
Task_Function()  
{  
    while(1){  
        wait_for_a_message();  
        handle_this_message();  
    }  
}  

如果没有该task感兴趣的事件发生,它就会一直处于阻塞的状态,直到有其它的任务或者中断IRQ给它发送了消息,才开始干活。
OS_Wait_For_Message的实现一般就是将该task的TCB挂到其等待的消息队列数据结构等待链表上,其它类似于OS_Task_Sleep,
7 接上一条,如果调用OS_Wait_For_Message进入阻塞态的task等的消息队列上有了消息之后,内核会唤醒这个task,将其重新加入就绪态链表上,如果该task的优先级是当前就绪态中最高的一个,那也需要启用一次任务调度,将CPU分配给该task。更多细节请参考 实时操作系统的任务调度示例之抢占
8 任务改变了自己或者其它task的优先级。一个任务当前正在运行中,所以它自己就是优先级最高的就绪态任务。如果任务降低了自己的优先级,就绪态中的其它任务优先级高于了自己,那么就需要发生任务切换;类似的,如果任务修改其它任务优先级同样也可能导致自己不再是最高优先级的task
9 多个同优先级task之间的轮转调度。大部分的RTOS都提供了同优先级task的轮转调度,尽量为它们公平分配CPU时间片。每次切换的时间点加上system tick中断到来的时刻,更多细节请参考 实时操作系统的任务调度示例之时间片
10 低优先级的任务发送消息或者信号触发高优先级的任务运行,这时低优先级就会被抢占,详细请参考 实时操作系统的任务调度示例之抢占
11 在ISR程序结束的时候,如果支持嵌套ISR,则就是所有ISR结束的时候。这是一种最为常见的调度点,因为嵌入式系统大部分的时候,所有的应用程序task都是阻塞的(运行的是idle task或者cpu干脆就处于低功耗停转的状态),各自等待着自己感兴趣的事件到来而做后续的处理,这些事件的起始触发点就是中断ISR,例如按键,或者串口、网卡等外设有数据到来。在ISR里,调度OS_Send_Message等方法来触发某个task对中断事件进行响应,这个详细的过程如下图所示:

你可能感兴趣的:(RTOS)