对于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实时操作系统学习笔记——内核结构和任务创建的更多相关文章

  1. ucos实时操作系统学习笔记——操作系统在STM32的移植

    使用ucos实时操作系统是在上学的时候,导师科研项目中.那时候就是网上找到操作系统移植教程以及应用教程依葫芦画瓢,功能实现也就罢了,没有很深入的去研究过这个东西.后来工作了,闲来无聊就研究了一下这个只 ...

  2. ucos实时操作系统学习笔记——任务间通信(信号量)

    ucos实时操作系统的任务间通信有好多种,本人主要学习了sem, mutex, queue, messagebox这四种.系统内核代码中,这几种任务间通信机制的实现机制相似,接下来记录一下本人对核心代 ...

  3. ucos实时操作系统学习笔记——任务间通信(消息)

    ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...

  4. ucos实时操作系统学习笔记——任务间通信(队列)

    ucos操作系统中的queue机制同样使用了event机制来实现,其实和前面的sem,mutex实现类似,所不同的是对sem而言,任务想获得信号量,对mutex而言,任务想获得的是互斥锁.任务间通信的 ...

  5. ucos实时操作系统学习笔记——任务间通信(互斥锁)

    想讲一下ucos任务间通信中的mutex,感觉其设计挺巧妙,同sem一样使用的是event机制实现的,代码不每一行都分析,因为讲的没邵贝贝老师清楚,主要讲一下mutex的内核是如何实现的.可以理解互斥 ...

  6. RTX51 Tiny实时操作系统学习笔记—初识RTX51 Tiny

     一,RTX51 Tiny简单介绍    RTX51 Tiny是一种实时操作系统(RTOS),能够用它来建立多个任务(函数)同一时候运行的应用(从宏观上看是同一时候运行的,但从微观上看,还是独立运行的 ...

  7. 操作系统学习笔记5 | 用户级线程 && 内核级线程

    在上一部分中,我们了解到操作系统实现多进程图像需要组织.切换.考虑进程之间的影响,组织就是用PCB的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...

  8. 操作系统学习笔记----进程/线程模型----Coursera课程笔记

    操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...

  9. 深挖计算机基础:趣谈Linux操作系统学习笔记

    参考极客时间专栏<趣谈Linux操作系统>学习笔记 核心原理篇:内存管理 趣谈Linux操作系统学习笔记:第二十讲 趣谈Linux操作系统学习笔记:第二十一讲 趣谈Linux操作系统学习笔 ...

随机推荐

  1. react组件的生命周期

    写在前面: 阅读了多遍文章之后,自己总结了一个.一遍加强记忆,和日后回顾. 一.实例化(初始化) var Button = React.createClass({ getInitialState: f ...

  2. C语言 · 矩阵乘法 · 算法训练

    问题描述 输入两个矩阵,分别是m*s,s*n大小.输出两个矩阵相乘的结果. 输入格式 第一行,空格隔开的三个正整数m,s,n(均不超过200). 接下来m行,每行s个空格隔开的整数,表示矩阵A(i,j ...

  3. 【WCF】自定义错误处理(IErrorHandler接口的用法)

    当被调用的服务操作发生异常时,可以直接把异常的原始内容传回给客户端.在WCF中,服务器传回客户端的异常,通常会使用 FaultException,该异常由这么几个东东组成: 1.Action:在服务调 ...

  4. redis 学习笔记(1)

    redis持久化 snapshot数据快照(rdb) 这是一种定时将redis内存中的数据写入磁盘文件的一种方案,这样保留这一时刻redis中的数据镜像,用于意外回滚.redis的snapshot的格 ...

  5. 11、Struts2 的文件上传和下载

    文件上传 表单准备 要想使用 HTML 表单上传一个或多个文件 须把 HTML 表单的 enctype 属性设置为 multipart/form-data 须把 HTML 表单的method 属性设置 ...

  6. 记录我这一年的技术之路(nodejs纯干货)

    2015年12月28日23:19:54 更新koa应用.学习型网站和开发者工具等 coding伊始 开始认认真真的学习技术还是2015.10.21日开始的,记得很清楚,那天,是我在龙湖正式学习的第一天 ...

  7. CGI与FastCGI nginx+PHP-FPM

    本文转载自CGI与FastCGI 1.当我们在谈到cgi的时候,我们在讨论什么 最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html. ...

  8. MyBatis源码分析(一)开篇

    源码学习的好处不用多说,Mybatis源码量少.逻辑简单,将写个系列文章来学习. SqlSession Mybatis的使用入口位于org.apache.ibatis.session包中的SqlSes ...

  9. iOS之应用版本号的设置规则

    版本号的格式:v<主版本号>.<副版本号>.<发布号>  版本号的初始值:v1.0.0 管理规则: 主版本号(Major version) 1.  产品的主体构件进 ...

  10. 二叉树的创建和遍历(C版和java版)

    以这颗树为例:#表示空节点前序遍历(根->左->右)为:ABD##E##C#F## 中序遍历(左->根->右)为:#D#B#E#A#C#F# 后序遍历(左->右-> ...