ucos实时操作系统学习笔记——内核结构和任务创建
对于ucos实时操作系统,邵贝贝的那本书已经写得很详细了,我因为之前不深的研究过ucos,所以在这里做一个笔记,写一些个人对该操作系统的理解,仅仅是个人理解,如果有人看到这边随笔有不对的地方,望给我指正。同时,锻炼一下自己组织语言的能力,有时候知道那么个意思,却总也说不出口。
ucos内种中有几个人变量比较重要,被贯穿在ucos内核的设计中。这几个变量中有在PCB中的局部变量,也有在整个系统内核设计中的全局变量。下面将分别介绍一下这几个变量。
首先,从OS_PCB中的局部变量讲起,如果去掉OS_TASK_CREATE_EXT_EN,OS_EVENT_EN等一些可选项的参数,ucos的OS_TCB结构体中主要的变量主要是这几个(简化OS_PCB结构体如下):
typedef struct os_pcb{
OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */
struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */
struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */
INT32U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */
INT8U OSTCBStat; /* Task status */
INT8U OSTCBStatPend; /* Task PEND status */
INT8U OSTCBPrio; /* Task priority (0 == highest) */
INT8U OSTCBX; /* Bit position in group corresponding to task priority */
INT8U OSTCBY; /* Index into ready table corresponding to task priority */
OS_PRIO OSTCBBitX; /* Bit mask to access bit position in ready table */
OS_PRIO OSTCBBitY; /* Bit mask to access bit position in ready group */
}OS_TCB;
从简化的的OS_PCB结构可看出其结构非常简单,主要包括以上几个部分:栈指针,当前任务所处TCB list的位置,描述任务delay的tick时间,任务状态,任务优先级以及计算当前任务position的四个变量。前面几个变量看一下注释应该可以很容易理解,最后四个变量需要稍作介绍,这也是整个内核结构中比较重要的部分。
接下来介绍一下几个关联比较密切的局部和全局变量,局部变量包括OS_PCB中的OSTCBX, OSTCBY, OSTCBBitX, OSTCBBitY,全局变量包括OSUnMapTbl, OSPrioHighRdy, OSRdyGrp, OSRdyTbl。
在OSTaskCreate的OS_TCBInit函数中有如下一段代码,可以从中看到创建任务的优先级和OS_PCB中四个局部变量的关系。操作系统根据任务优先级把任务分为不同的group,OSTCBY变量中存放的就是group的信息,每个group中有多少个任务则放在OSTCBX中。需要说明的是在ucos的内核设计过程中,通过宏定义将操作系统的可以创建的任务个数(OS_LOWEST_PRIO)分为64和256。在OS_LOWEST_PRIO是64的时候,会将任务分为8个group,每个group中最多会有8个任务,在如下代码中优先级<=63时,prio的最大值是63所以prio最大有6位,可以看到group和每个group任务的个数;在OS_LOWEST_PRIO是256的时候,会将任务分为16个group,每个group最多有16个任务。
通过全局变量OSRdyGrp来记录有属于哪个group的任务处于ready状态等待执行,为了降低操作系统内存的使用,通过Bit位代表每一个group并存储在OSRdyGrp中。OSRdyTbl则是代表属于哪个group中有多少个任务处于ready状态。为了更好的计算,每个操作系统在创建之初会提前计算好自己Bit位值,如下程序中OSTCBBITY和OSTCBBitX(OS_PRIO是根据OS_LOWEST_PRIO来计算定义的)。
#if OS_LOWEST_PRIO <= 63u /* Pre-compute X, Y */
ptcb->OSTCBY = (INT8U)(prio >> 3u);
ptcb->OSTCBX = (INT8U)(prio & 0x07u);
#else /* Pre-compute X, Y */
ptcb->OSTCBY = (INT8U)((INT8U)(prio >> 4u) & 0xFFu);
ptcb->OSTCBX = (INT8U) (prio & 0x0Fu);
#endif
/* Pre-compute BitX and BitY */
ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY);
ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);
上面介绍的几个全局变量中还有两个OSUnMapTbl, OSPrioHighRdy。OSUnMapTbl是一个匹配数组,数组中存放的是每个index值化成2进制,数值是1的bit位的位置是多少。当输入任务处于ready状态的group值时,可以得到那个group值最小;同理,当输入每个group中任务table时,可以得到这个group中优先级数据最小的任务,当然优先级数值越小代表优先级越高,从而通过取到的值经过计算得到优先级的数值。下面代码很好的诠释了OSPrioHighRdy这个是如何计算的。对于当OS_LOWEST_PRIO是256时,计算会复杂一些,当任务group或者table在8-16这个范围,会通过移位计算,因为OSUnMapTbl是按照8位一组设计的数组。
static void OS_SchedNew (void)
{
#if OS_LOWEST_PRIO <= 63 /* See if we support up to 64 tasks */
INT8U y; y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << ) + OSUnMapTbl[OSRdyTbl[y]]);
#else /* We support up to 256 tasks */
INT8U y;
INT16U *ptbl; if ((OSRdyGrp & 0xFF) != ) {
y = OSUnMapTbl[OSRdyGrp & 0xFF];
} else {
y = OSUnMapTbl[(OSRdyGrp >> ) & 0xFF] + ;
}
ptbl = &OSRdyTbl[y];
if ((*ptbl & 0xFF) != ) {
OSPrioHighRdy = (INT8U)((y << ) + OSUnMapTbl[(*ptbl & 0xFF)]);
} else {
OSPrioHighRdy = (INT8U)((y << ) + OSUnMapTbl[(*ptbl >> ) & 0xFF] + );
}
#endif
}
其次,介绍一下内核中几个比较重要的全局变量,如下所示:
OS_EXT OS_TCB *OSTCBCur; /* Pointer to currently running TCB */
OS_EXT OS_TCB *OSTCBFreeList; /* Pointer to list of free TCBs */
OS_EXT OS_TCB *OSTCBHighRdy; /* Pointer to highest priority TCB R-to-R */
OS_EXT OS_TCB *OSTCBList; /* Pointer to doubly linked list of TCBs */
OS_EXT OS_TCB *OSTCBPrioTbl[OS_LOWEST_PRIO + ];/* Table of pointers to created TCBs */
OS_EXT OS_TCB OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS]; /* Table of TCBs
1. OSTCBCur是一个OS_TCB类型的指针,指向当前正在运行的任务的TCB的地址;
2. OSTCBFreeList是一个指向没有使用的TCB列表的地址的指针;
3. OSTCBHighRdy指向当前处于当前处于ready状态的最高优先级的任务TCB地址的指针;
4. OSTCBList指向TCB列表的地址的指针;
5. OSTCBPrioTbl中存储的是对应优先级创建任务的TCB的地址,对于每一个优先级来说,当值是(OS_TCB *)1时,表示当前优先级reservd,当值时(OS_TCB *)0时,表示当前优先级可用;
6. OSTCBTbl是提前为每个优先级分配的TCB Table的内存。
再次,讲一下任务的创建,将通过简化代码,讲一下代码设计内容,此内容为个人理解,语言有可能"个人味"比较重,可以看一下邵老师对这块的分析。简化代码如下:
INT8U OSTaskCreate (void (*task)(void *p_arg),
void *p_arg,
OS_STK *ptos,
INT8U prio)
{
OS_STK *psp;
INT8U err;
OS_ENTER_CRITICAL();
if (OSIntNesting > 0u) { /* Make sure we don't create the task from within an ISR */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_CREATE_ISR);
}
if (OSTCBPrioTbl[prio] == (OS_TCB *)) { /* Make sure task doesn't already exist at this priority */
OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ... */
/* ... the same thing until task is created. */
OS_EXIT_CRITICAL();
psp = OSTaskStkInit(task, p_arg, ptos, 0u); /* Initialize the task's stack */
err = OS_TCBInit(prio, psp, (OS_STK *), 0u, 0u, (void *), 0u);
if (err == OS_ERR_NONE) {
if (OSRunning == OS_TRUE) { /* Find highest priority task if multitasking has started */
OS_Sched();
}
} else {
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = (OS_TCB *);/* Make this priority available to others */
OS_EXIT_CRITICAL();
}
return (err);
}
OS_EXIT_CRITICAL();
return (OS_ERR_PRIO_EXIST);
}
上面已经说过,OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()是成对存在的。任务创建时,会判断当前是否在中断中创建任务,如果是则会开中断,返回一个错误;然后判断当前优先级TCB是否被占用(if (OSTCBPrioTbl[prio] == (OS_TCB *)0) ),如果被占用,则开中断,返回优先级存在错误码;如果没有被占用,则设置当前任务的TCB table为reserved状态,说明当前任务优先级不能再被分配给其他任务,然后开中断;之后,会设置当前任务的堆栈地址和大小,用于切换任务时状态的保存和恢复;然后是对分配得到的TCB进行初始化,对TCB进行初始配置,简化参数主要包括上面提到OS_TCB中的参数配置,在对TCB初始化时,返回OS_ERR_NONE时,说明创建任务成功,这时候会重新进行最高优先级的选择,任务调度OS_Send(任务调度其实就是选择最高优先级的任务,然后将堆栈指针中的数据替换掉然后保存原来的CPU寄存器的过程),如果创建TCB初始化错误即任务创建错误,则会将分配的TCB释放掉,函数返回err。当前函数的临界区开关中断函数成对出现,当进入到子函数时,其实也是成对出现,关系比较清楚。
任务创建相对简单,将代码中一些可选的功能去掉后,代码就变得很简单清楚,需要什么功能,添加什么功能分析,会比较调条理,如果整体的去分析解读会显得比较乱,不利于梳理。
,
ucos实时操作系统学习笔记——内核结构和任务创建的更多相关文章
- ucos实时操作系统学习笔记——操作系统在STM32的移植
使用ucos实时操作系统是在上学的时候,导师科研项目中.那时候就是网上找到操作系统移植教程以及应用教程依葫芦画瓢,功能实现也就罢了,没有很深入的去研究过这个东西.后来工作了,闲来无聊就研究了一下这个只 ...
- ucos实时操作系统学习笔记——任务间通信(信号量)
ucos实时操作系统的任务间通信有好多种,本人主要学习了sem, mutex, queue, messagebox这四种.系统内核代码中,这几种任务间通信机制的实现机制相似,接下来记录一下本人对核心代 ...
- ucos实时操作系统学习笔记——任务间通信(消息)
ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...
- ucos实时操作系统学习笔记——任务间通信(队列)
ucos操作系统中的queue机制同样使用了event机制来实现,其实和前面的sem,mutex实现类似,所不同的是对sem而言,任务想获得信号量,对mutex而言,任务想获得的是互斥锁.任务间通信的 ...
- ucos实时操作系统学习笔记——任务间通信(互斥锁)
想讲一下ucos任务间通信中的mutex,感觉其设计挺巧妙,同sem一样使用的是event机制实现的,代码不每一行都分析,因为讲的没邵贝贝老师清楚,主要讲一下mutex的内核是如何实现的.可以理解互斥 ...
- RTX51 Tiny实时操作系统学习笔记—初识RTX51 Tiny
一,RTX51 Tiny简单介绍 RTX51 Tiny是一种实时操作系统(RTOS),能够用它来建立多个任务(函数)同一时候运行的应用(从宏观上看是同一时候运行的,但从微观上看,还是独立运行的 ...
- 操作系统学习笔记5 | 用户级线程 && 内核级线程
在上一部分中,我们了解到操作系统实现多进程图像需要组织.切换.考虑进程之间的影响,组织就是用PCB的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...
- 操作系统学习笔记----进程/线程模型----Coursera课程笔记
操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...
- 深挖计算机基础:趣谈Linux操作系统学习笔记
参考极客时间专栏<趣谈Linux操作系统>学习笔记 核心原理篇:内存管理 趣谈Linux操作系统学习笔记:第二十讲 趣谈Linux操作系统学习笔记:第二十一讲 趣谈Linux操作系统学习笔 ...
随机推荐
- PHP-生成缩略图和添加水印图-学习笔记
1.开始 在网站上传图片过程,经常用到缩略图功能.这里我自己写了一个图片处理的Image类,能生成缩略图,并且可以添加水印图. 2.如何生成缩略图 生成缩略图,关键的是如何计算缩放比率. 这里,我根据 ...
- shell变量
定义变量 定义变量时,变量名不加美元符号($),如: variableName="value" 注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样.同时,变量名 ...
- 随手记_C#验证码
前言 最近在网上偶然看见一个验证码,觉得很有意思,于是搜了下,是使用第三方实现的,先看效果: 总体来说效果还是可以的,官方提供的SDK也比较详细,可配置性很高.在这里在简单啰嗦几句使用方式: 使用步骤 ...
- JAVA问题集锦Ⅰ
1.Java的日期添加: import java.util.Date ; date=new date();//取时间 Calendar calendar = new GregorianCalendar ...
- 小兔JS教程(四)-- 彻底攻略JS数组
在开始本章之前,先给出上一节的答案,参考答案地址: http://www.xiaotublog.com/demo.html?path=homework/03/index2 1.JS数组的三大特性 在J ...
- 强强联合,Testin云测&云层天咨众测学院开课了!
Testin&云层天咨众测学院开课了! 共享经济时代,测试如何赶上大潮,利用碎片时间给女票或者自己赚点化妆品钱? 2016年12月13日,Testin联手云层天咨带领大家一起推开众测的大门 ...
- ASP.NET Core 中文文档 第四章 MVC(4.3)过滤器
原文:Filters 作者:Steve Smith 翻译:刘怡(AlexLEWIS) 校对:何镇汐 ASP.NET MVC 过滤器 可在执行管道的前后特定阶段执行代码.过滤器可以配置为全局有效.仅对控 ...
- wireshark 相关提示
Packet size limited during capture 提示说明标记的包没有抓全,在某些操作系统中,默认只抓96个字节,tcpdump中有"-s"参数可用于 ...
- python安装BeautifulSoup注意事项
好久没有写爬虫了,最近用Python的BeautifulSoup4.Scrapy分别对以前写的spider进行优化,发现python3.5后这些库变化了很多,遇到了许多问题,在这里做一下总结. 切换环 ...
- 浅谈C#网络编程(一)
阅读目录: 基础 Socket编程 多线程并发 阻塞式同步IO 基础 在现今软件开发中,网络编程是非常重要的一部分,本文简要介绍下网络编程的概念和实践. Socket是一种网络编程接口,它是对传输层T ...