ucos任务调度原理及任务就绪表
之前我们说到,系统在运行的时候会直接依靠任务的优先级来找到任务的控制块从而实现任务的调用切换等功能,那么接下来的问题就是,系统是怎么找到并确定某一个特定的最高优先级任务并确定他的优先级的呢
为了解决这个问题,ucos采用了一种比较巧妙地方式,叫做就绪任务表,定义如下
OS_EXT OS_PRIO OSRdyTbl[OS_RDY_TBL_SIZE];
可以见到,就绪任务表的大小为OS_RDY_TBL_SIZE, OS_RDY_TBL_SIZE展开就是
#define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 8u + 1u)
也就是系统(最低优先级任务/8)+1,我们可以认为当有63个任务的时候,就绪表的数据大小为8,每个元素的大小为
#if OS_LOWEST_PRIO <= 63u
typedef INT8U OS_PRIO;
#else
typedef INT16U OS_PRIO;
#endif
当小于63的时候为八位,大于63为16位,为便于分析,我们假设当前系统任务数量小于63
另外为了解析任务就绪表,还需要知道一个变量
OS_EXT OS_PRIO OSRdyGrp;
该变量是将任务控制表分组的变量
任务控制表的原理为使用数据的位标识特定的优先级的任务reday,假如有63个优先级,那么一共就有8个OSRdyTbl元素,每个元素八位,正好有64个位来存放,同时,则64个位分为八组,每组就是一个字节,对应OSRdyGrp的一个位,这样,通过OSRdyGrp中的位就能找到相应的reday任务位于哪个数组元素中,通过元素的哪一位为1就能确定哪一位中断发生,此时,我们就有了两个值
此时,我们就可以根据优先级找到任务在就绪表中的位置,因为prio最大为63,即二进制00111111,将345位指明变量OSRdyGrp,000b-111b分别代表0-7位 012位指明该数组元素的具体数据位,也是000b-111b分别代表0-7位,就能直接确定当前这个优先级两个数据中的位置
举例说明如果某个优先级00001100(12)的任务准备好,那么OSRdyGrp的第一位为1, OSRdyTbl[1]的第4位为1,同样可以反推.
当遇到多个准备好的任务的时候,因为系统总会选用最高优先级的任务,而最高优先级的任务数值是最小的,所以能迅速选择出当前最高优先级的任务
下面看代码,当系统设置某个任务为ready的时候,代码如下
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {
OSRdyGrp |= ptcb->OSTCBBitY; OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
这里面用到了几个变量,是存放在任务的tcb中的
OSTCBBitY OSTCBBitX OSTCBY初始化任务的时候被赋值,如下
ptcb->OSTCBY = (INT8U)(prio >> 3u);
ptcb->OSTCBX = (INT8U)(prio & 0x07u);
ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY);
ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);
可以看出, OSTCBY是针对于OSRdyGrp的变量,他将优先级的345位变成了bcd码的数据OSTCBBitY, OSTCBX是针对于OSRdyTbl的,将012位解析成为OSTCBBitX,这样置位的时候只要相互或一下就能将reday设置好,举例来说,还是刚才的00001100(12),计算出来
OSTCBY = 1 OSTCBBitY = 0b00000010 OSTCBX = 4 OSTCBBitX = 0b00010000
经过下面的运算
OSRdyGrp |= ptcb->OSTCBBitY; OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
和之前我们饿推理是不是一模一样?满巧妙地,
设置1的方法有了清零就显而易见的通过与非实现
pevent->OSEventTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX; /* Remove task from wait list */
if (pevent->OSEventTbl[y] == 0u) {
pevent->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
}
当从reday分组中根据表取出最高优先级的任务优先级的方法为
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
首先获取分组,使用了一个静态数组,数据如下
INT8U const OSUnMapTbl[256] = {
0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F */
6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F */
7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF */
6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u /* 0xF0 to 0xFF */
};
乍一看数组OSUnMapTbl的真实意义不明显,但是仔细分析就可以看出,这个数组实际上是将一个八位数据中的为1的最低位的位置找出来,所以他的设置规律我们可以看到,凡是下标的二进制码的第0为为1设置为0,第一位为1设置为2…第七位为1设置为7,大家可以验证,我验证一下7,第七位为1而且是最低位的话只有一个数0b10000000(其他位都必须是0),这个时候对应的数组元素应该是7,元素位置0x80,可以正好对上.要分析明白这一段始终要记得一个原则,ucos的优先级数值越低,优先级越高,所以寻找最高优先级必然是从最低位开始找起.
相当于就是说这个数据能够快速将bcd码转换为ascii码,从而避免的循环,避开了实时操作系统的大忌(运行时间不可测).
在得到两个数据(一个优先级分组一个分组优先级)之后,将两个数据相互组合,就能得到系统的最低优先级了.
所以
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
很巧妙地方式.
Ucos的任务创建采用两个函数
OSTaskCreate
OSTaskCreateExt
之前我们已经说了创建任务先做堆栈初始化,然后做tcb任务控制块初始化,接下来的工作如下
if (err == OS_ERR_NONE) {
if (OSRunning == OS_TRUE) {
OS_Sched();
}
} else {
检测系统是否处在运行状态,在运行状态就触发一次任务调度,这次调度的基础还是在tcb_init中,如下
OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run*/
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OSTaskCtr++; /* Increment the #tasks counter */
创建任务的时候默认将任务设置为运行状态,在rdytab中挂好标志,这样接下来调度器就能根据任务的优先级和当前操作系统状态来决定是否执行该任务,如果创建任务优先级低于当前正在执行的任务,那么就会等待
使用ext方法创建数据的时候,比普通的创建方法多了几个参数,这些参数分别是
任务的标示id 任务堆栈栈底指针 任务堆栈容量 附加数据域的指针 操作选项option
这些参数的功能在于运行时可以实时的监控任务
另外,千万记得很重要的一点,ucos不能在中断中创建任务
任务的挂起和恢复采用两个函数
OSTaskSuspend 任务挂起
OSTaskResume 任务恢复
任务挂起的流程如下
检测要挂起的是不是空闲任务,空闲任务不能挂起
检测当前挂起任务时挂起其他别的任务还是挂起自身(挂其自身需要设置挂起优先级为0xff OS_PRIO_SELF),接着按照优先级从系统tcb表中获取需要挂起的任务tcb,,通过ptcb快速的将系统就绪表中的挂起任务的就绪状态清除,然后引发一次调度,从而实现挂起,同时要设置tcb中的OSTCBStat为OS_STAT_SUSPEND,防止在后面的调度中任务又被恢复,这里面有一个需要判断挂起的是不是自身的问题,如果挂起的是自身,需要在挂起之后调度一次,从而完成真正的骨气,而如果不是当前运行的任务本身,则挂起之后是不需要引发调度的,设置好标志即可
知道了挂起流程,恢复流程相反就可以了,首先检测优先级的合法性,然后获取要挂起的任务的tcb指针,判断任务存在,并且任务当前处于被挂起状态并且等待时间为0,条件全部符合的话,取消任务在控制块中的挂起记录,并且调用一次调度器,因为可能恢复的任务优先级比当前优先级高,需要切换
修改任务优先级
OSTaskChangePrio
该函数能实时的修改任务的优先级,主要修改了几个部分,tcb中的优先级,重新修改了系统的就绪表(优先级和就绪表是有着一一对应关系的),以及tcb中用于快定位的几个元素.另外,对于与自身相关的时间信号灯都做了操作,最后使用调度器调度
删除任务
OSTaskDel
OSTaskDelReq
前一个用来任务删除自身或者除了空闲任务之外的其他任务
后一个用来a任务向b任务请求删除b任务
第一个函数的流程是首先从系统就绪表中删除就绪的该优先级任务,然后将删除任务的ptcb清零(堆栈不用,因为下一次设置堆栈的时候会重新赋值),最后从系统tcb控制链表中删除掉指定任务的tcb,并将这个tcb切换到空闲tcb控制块链表中,最后调用调度器,执行调度
而第二个任务的删除则不是实时进行的,他只是将tcb中的状态设置了一下,当被删除任务被调用的时候,再去执行的删除过程,代码如下
ptcb->OSTCBDelReq = OS_ERR_TASK_DEL_REQ;
如果任务自身向自身发送请求删除的话,系统会返回当前任务请求删除的状态
stat = OSTCBCur->OSTCBDelReq;
如果是OS_TASK_DEL_REQ,那么任务应当在合适的时机删除自身
这两种情况是为了更好地解决资源管理的问题,有时候删除的任务被分配了某些资源,但是操作系统删除任务的时候并没有释放这些资源,会造成资源的丢失或者系统的运行故障,所以用请求删除的方式,何时删除有任务自己决定,就不会出现这些问题了,而被删除放应当在程序中调用OSTaskDelReq函数,参数使用OS_PRIO_SELF,获取其他任务对自身的删除请求,从而决定某些操作
例子
请求方 while(OSTaskDelReq(44)==OS_TASK_NOT_EXIST){OSTimeDly(1);}循环等待任务删除
被请求方if(ISTaskDelReq(OS_PRIO_SELF==OS_TASK_DEL_REQ))
{
//释放系统资源
OSTaskDel(OS_PRIO_SELF);//执行真正的任务删除
}
查询任务的信息
OSTaskQuery
用于获得指定任务的tcb状态信息,,该函数返回值为IS_NO_ERR则返回信息可用
任务返回
OS_TaskReturn
该函数在使能任务删除的情况下会先执行任务删除,删除自身,之后死循环延时,延时一般运行不到,因为任务删除的之后系统调度到新任务了.
ucos任务调度原理及任务就绪表的更多相关文章
- ucos任务优先级从64到256,任务就绪表的改变
Ucos在任务调度中经常使用的技术为任务就绪表,在之前的文章中使用的例子是低于64个优先级的任务就绪表查找方法,现在ucos将任务扩展到256优先级之后,任务就绪表的查找也做了一定的修改,今天来讲讲 ...
- μC/OS-II 任务就绪表及任务调度
任务调度 多任务操作系统的核心工作就是任务调度. 所谓调度,就是通过一个算法在多个任务中确定该运行的任务,做这项工作的函数就叫做调度器. μC/OS-II 进行任务调度的思想是 "近似地每时 ...
- ucosii --任务就绪表
任务就绪表的任务就是高效的找出当前优先级最高的就绪任务. 由任务就绪表OSRdyTbl和任务就绪组OSRdyGrb组成,OSRdyTbl每一个位都记录着一个任务的就绪状态, 0非就绪1就绪,OSRdy ...
- UCOSii任务就绪表之OSUnMapTbl[16*16]的数组是如何得到的
我比较喜欢图,如下图: 图1: INT8U const OSUnMapTbl[]数组内的数据. 1.UCOSii的优先级相关内容 首先先介绍一个概念:优先级.UCOSii的优先级按倒叙排列,即优先级数 ...
- μC/OS中的任务就绪表
为了便于对就绪表的查找,μC/OSII又定义了一个数据类型为INT8U的变量OSRdyGrp, 并使该变量的每一位都对应OSRdyTbl[ ]的一个任务组(即数组的一个元素),如果某任务组中 有任务就 ...
- 交换机工作原理、MAC地址表、路由器工作原理详解
一:MAC地址表详解 说到MAC地址表,就不得不说一下交换机的工作原理了,因为交换机是根据MAC地址表转发数据帧的.在交换机中有一张记录着局域网主机MAC地址与交换机接口的对应关系的表,交换机就是根据 ...
- JQuery基础原理 与实例 验证表单 省市联动 文本框判空 单选 复选 判空 下拉判空 确认密码判等
JQuery 基础原理 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> & ...
- Oracle数据库基本知识-原理,实例,表空间,用户,表
1.数据库原理及sql 数据库:是人们存放数据,访问数据,操作数据的存储仓库. DB:数据库,按存储结构来组织,存储和管理的数据仓库 DBMS:数据库管理系统,管理数据库的软件 SQL:结构化查询语言 ...
- 编译原理LL1文法分析表算法实现
import hjzgg.first.First; import hjzgg.follow.Follow; import hjzgg.tablenode.TableNode; import hjzgg ...
随机推荐
- mysql 修改 添加 删除 表字段
添加表的字段 alter table 表名 add 字段名 字段的类型 例子: alter table table1 add transactor varchar(10) n ...
- sizeof和strlen
1.char *str="0123456789"; 这个变量是存在静态区域的,是delete不了的,是内存自动分配的,可以用strlen(str)得到其长度,不能用sizeof. ...
- 多校 Babelfish
题目链接:http://acm.hust.edu.cn/vjudge/contest/124435#problem/A 密码:acm Sample Input dog ogday cat atcay ...
- IDL 遍历 XML文档示例
IDL解析XML文档同样也有2种方法:DOM和SAX方式:两种方法在IDL自带的帮助里面有详细介绍,可以去查看. IDL 源码PRO sample_recurse, oNode, indent COM ...
- UVALive 6887 Book Club
最大流,有向环覆盖问题. #include<cstdio> #include<cstring> #include<string> #include<cmath ...
- jquery选择器 之 获取父级元素、同级元素、子元素(转)
一.获取父级元素 1. parent([expr]): 获取指定元素的所有父级元素 <div id="par_div"><a id="href_fir& ...
- Redis学习笔记(二)-key相关命令【转载】
转自 Redis学习笔记(二)-key相关命令 - 点解 - 博客园http://www.cnblogs.com/leny/p/5638764.html Redis支持的各种数据类型包括string, ...
- 【转】调用getActionBar()报Call requires API level 11 (current min is 8): android.app.Activity#getActionBar
解决办法: 第一种方法:修改AndroidManifest.xml中的minSdkVersion=11 第二种方法: 1.导入android-support-v7-appcompat项目,并将其作 ...
- lpr
http://flatline.cs.washington.edu/orgs/acm/tutorials/printing/index.html
- 利用XShell 上传和下载文件
原文:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=28977986&id=4292781 借助XShell,使用li ...