讲解UCOSII嵌入式操作系统的任务调度策略
其实整个UCOSII嵌入式操作系统的任务调度策略便是如此,现在来进行一个总结:
①初始化完毕以后,系统启动,某个任务在执行中时,每隔一定周期发生滴答时钟中断,系统会在中断中遍历整个任务链表,更新每个任务的延时时间,并且修改就绪状态。
②当当前任务执行完毕后,进入延时函数或者被挂起,在阻塞中会把当前任务挂起(清空当前任务的就绪状态,使其进入未就绪状态),然后根据查表法找到在就绪任务中,优先级最高的那一个新任务。
③找到新任务以后,人工强制发生一个中断,保存上个任务的堆栈信息,弹出下个任务的堆栈信息,同时更改PC指针,进行任务切换。
经过以上三个步骤,便可以完成从A任务到B任务的调度。
现在回到第一篇文章中提出的那个问题:UCOSII到底是如何保证它的实时性的呢?
如果任务的调度都是发生在当前任务进入延时之后,似乎操作系统根本无法保障自身的实时性。
比如说一个优先级最低的任务由于某些处理非常耗费时间,它一直无法进入延时,导致无法进入任务切换,那么优先级高的任务即使已经就绪,反而是一直都无法被执行了……
同样在第一篇中说过,UCOSII系统除了在当前任务进入延时函数会发生调度之外,还有别的时机会进行任务切换:
1.当前任务进入了延时。
2.当前任务被挂起。
3.当前任务执行时,发生了某些中断。
第1点我们已经讲完,第2点非常好理解,我们现在看一个函数:OSTaskSuspend()
这个函数的作用,是把某个任务挂起(也就是不进行调度暂停执行,直到它被放下来),现在来分析一个实例:
有一个任务调用了这个函数:
void App1_task(void *pdata)
{while(1){if (OS_ERR_NONE != OSTaskSuspend(OS_PRIO_SELF)){Dbg_SendStr("App1_task Suspend Error£¡\r\n");}delay_ms(10);};
}
当前任务执行了红色代码之后,便会把自身挂起来,如果没有再别的地方对它进行激活,这个任务大概是永远也不会执行下去了。
分析OSTaskSuspend函数:
INT8U OSTaskSuspend (INT8U prio)
{BOOLEAN self;OS_TCB *ptcb;INT8U y;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */OS_CPU_SR cpu_sr = 0u;
#endif#if OS_ARG_CHK_EN > 0uif (prio == OS_TASK_IDLE_PRIO) { /* Not allowed to suspend idle task */return (OS_ERR_TASK_SUSPEND_IDLE);}if (prio >= OS_LOWEST_PRIO) { /* Task priority valid ? */if (prio != OS_PRIO_SELF) {return (OS_ERR_PRIO_INVALID);}}
#endifOS_ENTER_CRITICAL();if (prio == OS_PRIO_SELF) { /* See if suspend SELF */prio = OSTCBCur->OSTCBPrio;self = OS_TRUE;} else if (prio == OSTCBCur->OSTCBPrio) { /* See if suspending self */self = OS_TRUE;} else {self = OS_FALSE; /* No suspending another task */}ptcb = OSTCBPrioTbl[prio];if (ptcb == (OS_TCB *)0) { /* Task to suspend must exist */OS_EXIT_CRITICAL();return (OS_ERR_TASK_SUSPEND_PRIO);}if (ptcb == OS_TCB_RESERVED) { /* See if assigned to Mutex */OS_EXIT_CRITICAL();return (OS_ERR_TASK_NOT_EXIST);}y = ptcb->OSTCBY;OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX; /* Make task not ready */if (OSRdyTbl[y] == 0u) {OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;}ptcb->OSTCBStat |= OS_STAT_SUSPEND; /* Status of task is 'SUSPENDED' */OS_EXIT_CRITICAL();if (self == OS_TRUE) { /* Context switch only if SELF */OS_Sched(); /* Find new highest priority task */}return (OS_ERR_NONE);
}
直接从这句if (prio == OS_PRIO_SELF) 代码部分开始看,他首先要判断一下即将挂起的任务是不是自己,现在我们传的参数就是OS_PRIO_SELF,所以它应该执行第一个if判断。
在这个if判断中,保存了需要挂起的任务的优先级,然后用if (ptcb == (OS_TCB *)0)代码判断一下需要挂起的任务是否存在(由于我们挂起的是自身,自身肯定是存在的,但是这并不表示这个判断多余,因为如果是一个优先级为1的任务调用这个函数去挂起一个优先级为2的任务,那判断一下还是很必要的)。
然后接下来的几句代码就不用再解释了,和任务进入延时函数把自己的就绪状态情况是一毛一样的处理。
直接看ptcb->OSTCBStat |= OS_STAT_SUSPEND这句代码,变量OSTCBStat 很容易理解,它表示当前任务的状态,整句代码的意义就是给当前任务设定一个已经被人工挂起了的状态,免得在任务调度的时候被调度出来(在滴答时钟中断中有这个变量的判断)。
这句代码以后:
if (self == OS_TRUE) { /* Context switch only if SELF */OS_Sched(); /* Find new highest priority task */}
这几句代码也已经很熟悉了,中间那个函数就是任务切换,先看看那个判断,如果我要挂起的是当前任务,那么就立即进行切换,如果挂起的是别的任务,那就不用切换,这个理解起来应该不难。
在理解第一种切换时机的前提下,第二种任务切换的时机很好理解,但是第二种任务切换的时机似乎仍然不能保证任务执行的实时性,如果低优先级的任务既不进入延时,也不被挂起,那么高优先级的任务即使就绪了,依然无法执行。
现在来看第三种,当中断发生时,任务切换。
在任务执行期间,发生的最频繁的中断必然就是滴答时钟中断,现在重新回到以前看过的那个中断服务函数:
void SysTick_Handler(void)
{if(delay_osrunning==1) //OS开始跑了,才执行正常的调度处理{OSIntEnter(); //进入中断OSTimeTick(); //调用ucos的时钟服务程序OSIntExit(); //触发任务切换软中断}
}
这一次的重点不再是第二个函数,而是第一个和第三个函数:OSIntEnter,OSIntExit。
这两个函数总是成对出现,从函数名便可看出,OSIntEnter是进入中断时候调用,OSIntExit是离开中断时候调用。
由于滴答时钟中断是周期性发生,因此这两个函数也会被周期性被调用
OSIntEnter的定义如下:
void OSIntEnter (void)
{if (OSRunning == OS_TRUE) {if (OSIntNesting < 255u) {OSIntNesting++; /* Increment ISR nesting level */}}
}
这个入口函数的处理很简单,就是对变量OSIntNesting执行加处理,表示我现在正在执行中断函数,如果发生了中断,或者有中断嵌套,那么这个变量肯定是大于0,在系统的很多地方,都需要判断这个变量,因为很多处理都不能在中断中执行。
出口函数的定义就有些复杂了:
void OSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */OS_CPU_SR cpu_sr = 0u;
#endifif (OSRunning == OS_TRUE) {OS_ENTER_CRITICAL();if (OSIntNesting > 0u) { /* Prevent OSIntNesting from wrapping */OSIntNesting--;}if (OSIntNesting == 0u) { /* Reschedule only if all ISRs complete ... */if (OSLockNesting == 0u) { /* ... and not locked. */OS_SchedNew();OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
#if OS_TASK_PROFILE_EN > 0uOSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */
#endifOSCtxSwCtr++; /* Keep track of the number of ctx switches */OSIntCtxSw(); /* Perform interrupt level ctx switch */}}}OS_EXIT_CRITICAL();}
}
直接从if (OSRunning ==OS_TRUE)部分开始看,首先判断系统是否在运行,在系统运行的前提下,对变量OSIntNesting进行减处理。
在进入中断之时,我们调用了入口函数,对变量OSIntNesting加1,中断内容处理完以后,出口函数对变量OSIntNesting减1,当变量OSIntNesting为0的时候,表示没有进行中断处理,这个时候才可以进行任务切换。
if (OSLockNesting == 0u) { /* ... and not locked. */OS_SchedNew();OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
#if OS_TASK_PROFILE_EN > 0uOSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */
#endifOSCtxSwCtr++; /* Keep track of the number of ctx switches */OSIntCtxSw(); /* Perform interrupt level ctx switch */}}
然后判断一下系统是否上锁,如果上锁了,任然不能进行调度。
当一切条件就绪以后,调用函数OS_SchedNew,这个函数也已经很熟悉了,作用就是寻找在就绪任务中,优先级最高的那一个。
把优先级最高的任务保存在OSPrioHighRdy中,如果当前任务不等于优先级最高的任务,那么就调用系统函数OSIntCtxSw进行任务切换……
看到这里,现在应该能够回答那个问题了吧?
如何保证操作系统的实时性?
void SysTick_Handler(void)
{if(delay_osrunning==1) //OS开始跑了,才执行正常的调度处理{OSIntEnter(); //进入中断OSTimeTick(); //调用ucos的时钟服务程序OSIntExit(); //触发任务切换软中断}
}
在中断服务函数中,第二个函数负责更新任务就绪表,第三个函数任务负责切换任务,因为滴答中断是周期性发生的(每间隔x毫秒),所以任务切换也是周期性发生的。
当有一个优先级低的任务执行时,如果有优先级更高的任务就绪了,那么只要发生了一次滴答中断,任务就能被立即切换过去,延时最多有一个滴答时钟的周期。
如果定义的时钟周期是1ms,那么低优先级的任务最多也就能运行1ms,然后便会强行剥夺CPU的执行权限,转交给高优先级的任务。
由于存在这种机制,因此便能保证UCOSII系统任务的实时性。
总结
个人认为,对于一个嵌入式操作系统而言,最核心和最重要的,便是任务调度的机制与策略,只要实现了这个功能,那么一个嵌入式操作系统的架构也就搭建了起来,至于其他的消息,邮箱,队列等功能,都是在这个架构上实现增值产品。
只要深入理解了UCOSII系统的任务调度的原理,那么自己手动实现一个简易的操作系统内核,似乎也并不是一件触不可及的事情。
如果有兴趣,可以去这里看看(https://www.zhihu.com/question/25628124)里面有很多大神都实现了自己的操作系统。
全文结束!
谢谢各位的支持,如果本文对各位有丝毫的帮助或者启发,恳请点赞支持。
如果各位在本文中发现了任何纰漏和错误,也希望不吝赐教,请在评论中直接指出。
讲解UCOSII嵌入式操作系统的任务调度策略相关推荐
- 手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(五)
继续...... 整个UCOSII嵌入式操作系统的任务调度策略便是如此,现在进行一个总结: 某个任务在执行中,每隔一定周期发生滴答时钟中断,在中断中遍历整个任务链表,更新每个任务的延时时间,修改就绪状 ...
- 手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(一)
刚参加工作那几年做MCU程序,由于实现的功能和需求都比较简单,外围模块也很少,所以大多数的项目直接就在裸机上写代码.当时也没有任务和线程的概念,脑子里想的只有单个函数的调度,变量的控制等等.工作时先把 ...
- 手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(二)
继续-- if (ticks > 0u) { /* 延时参数是否为0 */OS_ENTER_CRITICAL(); /* 禁止中断 */y = OSTCBCur->OSTCBY;OSRdy ...
- 手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(三)
继续...... if (ticks > 0u) { /* 0 means no delay! */OS_ENTER_CRITICAL();y = OSTCBCur->OSTCBY; /* ...
- 手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(四)
继续...... 再回到那个重要得函数: void OS_Sched (void) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU ...
- 深入研究嵌入式操作系统的绝佳教材
前 言 本书作者多年的嵌入式领域研发.管理和教学经验,促成了本书的诞生. 本书的目的 操作系统是一个古老的话题,它的出现和发展,对于计算机技术来说,意义非凡.这种发展不仅仅体现在传统的计算机中,在强调 ...
- 三种嵌入式操作系统的分析与比析
1.1 嵌入式系统 嵌入式系统是以嵌入式计算机为技术核心,面向用户.面向产品.面向应用,软硬件可裁减的,适用于对功能.可靠性.成本.体积.功耗等综合性能有严格要求的专用计算机系统. 嵌入式系统应具有的 ...
- 嵌入式操作系统的主要特点都有哪些
嵌入式操作系统(EOS)是指用于嵌入式系统的操作系统.嵌入式操作系统是一种用途广泛的系统软件,通常包括与硬件的底层驱动软件.系统内核.设备驱动接口.通信协议.图形界面.标准化浏览器等.嵌入式系统分为4 ...
- 嵌入式操作系统的基本概念
一什么是嵌入式系统 嵌入式系统一般指非pc系统,有计算机功能但又不称之为计算机的设备或器材.它是以应用为中心,软硬件可裁减的,适应应用系统对功能.可靠性.成本.体积.功耗等综合性严格要求的专用计算机系 ...
- 视频教程-韦东山生活实例演绎法讲解蓝牙-嵌入式
韦东山生活实例演绎法讲解蓝牙 2003 年毕业于中国科学技术大学,电子专业.软件专业双学位.近10年嵌入式开发经验,曾ZTE公司负责Linux底层系统开发. 近5年作为特聘讲师在若干个有名的培训机构讲 ...
最新文章
- 【Android】Vitamio 4.0 公测版发布(2013-05-28)
- 深入理解Objective-C:Category
- Python---利用蒙特.卡罗方法计算圆周率近似值
- 2n皇后问题(dfs)
- TextureView实现视频播放
- RoboMaster电机驱动
- 【图文说明】屏幕录像专家如何安装、录制小文件的录像
- Sqlserver-循环执行sql语句
- Hexo博客与Next主题的高级应用
- 微信java精简版低内存_微信精简版apk下载-微信精简版低内存2016 安卓版_5577安卓网...
- word中出现“由于文件许可权错误,word无法完成保存操作”的解决办法
- 【小技巧】STA静态时序分析概述
- 一种自动将3DMax模型转换为UE4直接可用的模型资源的方法与流程
- Android persist类property 知识点
- Elastic Stack(ELK6.6.2)安装及使用
- 【NLP】文献翻译1——基于结构和词嵌入的文本相似性测量方法
- jsp观影平台/影视观看系统/视频网站
- 深度学习之目标检测综述
- 关于JDK8和JDK11切换问题
- 奥比中光深度摄像头_奥比中光深度摄像头NiViewer.exe