小言_互联网的博客

ucosiii实时嵌入式操作系统时间片实现原理深入解析

219人阅读  评论(0)

ucosiii等系统需要一个系统节拍,这个由CPU的比如stm32单片机的SysTick系统滴答定时器提供,一般而言操作系统的时钟节拍的周期tick=1ms(如果太小,即产生的节拍频率越高,系统任务切换发生的约频繁,那么系统由于任务切换调度等系统操作对耗费的系统CPU时间就越多,增加了系统内核的负担,但是增强了系统实时性。如果太大就降低了系统实时性。),所以设置系统滴答定时器频率为1000hz即可,即1ms产生一次定时中断。ucosiii系统支持同一优先级下存在多个任务,那么每一个任务可以分配不同的时钟节拍个数,即该任务的时间片。

ucosiii有多个列表,最重要的是两个(其他的还有任务等待列表等),时钟节拍列表(tick list,也叫时基列表)包含了所有处于延时的任务、任务就绪列表(ready list)包含了所有具备立马运行条件的任务。系统中的每个正常运行(除开任务被挂起,删除等情况)的任务要么在就绪列表里要么在时钟节拍列表里,即要么是可以运行要么就是在延时。也就是这两个列表没有交集

时钟节拍列表在代码层面的实现是由全局数组OSCfg_TickWheel[]和全局变量OSTickCtr构成,如下图所示,数组的每个元素称为一个表项(Entries)(有多个表项的原因是方便update时候的优先级取模映射,具体可以看看书籍),而每个表项都带领一个按照延时节拍数升序排列的任务控制块TCB构成的双向链表。

如果某个任务调用了系统延时函数OSTimeDly(n),就会根据其优先级取模运算映射到上面哪一个表项,然后根据其延时的值节拍数按照升序排列将其TCB插入到链表中,这样做的目的是方便这个时钟节拍表的更新update(每个时钟节拍中断到来,都会来这个列表把所有任务的延时值TickRemain递减一个,同时进行是否有任务延时结束的判断,然后把它从这个表删除,并把它加入任务就绪表)。

任务就绪表也是由两个全局变量构成,优先级位映射表OSPrioTbl[](动态变化的),用来记录哪个优先级下有任务就绪。

OSReadyList[]用来记录每一个优先级下所有就绪的任务,也是分为多个表项,每个表项带领一个TCB双向链表(如果是ucosii,不支持同一优先级下多个任务,所以ucosii没有这个OSReadyList[]数组,仅仅有优先级位映射表OSPrioTbl[]罢了)。

如果有任务具备就绪条件(比如新建的任务或者延时完成的任务),就通过优先级取模映射到对应的优先级表项下插入这个TCB节点,同一个优先级可以对应多个任务,如下图所示。

所以,系统调度器函数调度的过程:首先根据优先级映射表获得哪个优先级有任务就绪,然后根据此优先级(区别于时钟节拍列表,不用取模映射)在OSReadyList[]对应的表项的链表的第一个TCB节点任务获得运行。如果此优先级下只有一个任务或者多个任务都仅仅第一个节点任务获得运行,每一次系统节拍到达(即系统定时器中断发生),都会在定时器中断函数OS_CPU_SysTickHandler()里通过发送一个消息给一个很高优先级的任务OS_TickTask()(这样是防止此中断函数运行时间过长,影响了系统整体的实时性,所以中断里面的耗时操作任务化,所以中断函数里不直接处理这些耗时操作,由于时钟节拍任务不是最高优先级,那么节拍中断函数给它发了消息,它就不一定马上能执行,那么就会造成时钟节拍列表更新不准确了,使得任务延时不准确了,网上确实也有人遇到这个问题,后面会有说明)。这个OS_TickTask()任务里面更新时钟节拍列表(即哪些延时任务的TickCtrMatch跟全局的OSTickCtr相等了,说明延时到期了,就把这个tcb从这个时钟节拍列表删除,插入就绪表)。然后中断函数里面还调用OS_SchedRoundRobin()函数(里面并没有发生任务调度),此进行此任务的时间片剩余数减1,如果减为0,就把它插入这个链表的末尾,第二的TCB节点任务就变成了此链表的第一个节点了,然后调用OSIntExit()里的OSIntCtxSw()函数进行任务切换(具体过程可以看我这篇文章,ucosii/iii实时嵌入式操作系统任务切换与中断管理深入解析),那么根据前面的仅仅第一个节点获得运行规则可知,现在第二的这个TCB节点任务获得运行了(如果没有更高优先级处于就绪),如此循环轮转调度。其ucosiii源码关键的函数调用关系如下图所示。

 

其实对于这个时钟节拍列表更新不准确的问题,其解决办法是把时钟节拍任务设为非常非常高的优先级,比所有任务(除了中断服务管理任务 OS_IntQTask())都高,设为1即可,我也是参考别的博客,发现确实是这样的,如下图。默认下载的官方的ucosiii的代码,时钟节拍任务默认的优先级是10,这样是不行的,如果有用户任务的优先级更高,那么时钟节拍列表更新就不准确了,那么就会延时不准确等其他莫名其妙的情况。

 

我觉得野火那本书写的是对的,直接放中断里更新时钟节拍列表,这样一定能够每次节拍中断到来,都可以马上更新,如下图。

下面是一个网友遇到的问题,我贴出来,我们来分析一下:

这个问题,明显是因为任务2的优先级高于了系统时钟节拍任务的优先级,所以导致时钟节拍列表(管理所有任务延时的)无法每个节拍中断到来后及时更新,所以任务1虽然优先级更高但是因为在延时状态,即处于时钟节拍列表里,因为这个表无法及时更新,导致任务1延时到了也无法被插入就绪表,所以,任务1就无法正确的0.5s打印一次。其解决办法就是更改时钟节拍任务的默认优先级为1,这样系统中除了中断服务管理任务 OS_IntQTask()为0最高优先级,它就是系统第二高的优先级了,可以确保基本准确的更新时钟节拍表了。下面看看网友的解答,果然是这个原因。

参考文章

UCOSIII优先级

UCOSIII的系统任务

UCOSIII 任务优先级对任务抢占的影响


转载:https://blog.csdn.net/kangkanglhb88008/article/details/101979717
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场