对于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. .Net Core MVC 网站开发(Ninesky) 2.4、添加栏目与异步方法

    在2.3中完成依赖注入后,这次主要实现栏目的添加功能.按照前面思路栏目有三种类型,常规栏目即可以添加子栏目也可以选择是否添加内容,内容又可以分文章或其他类型,所以还要添加一个模块功能.这次主要实现栏目 ...

  2. Unity游戏内版本更新

    最近研究了一下游戏内apk包更新的方法. ios对于应用的管理比较严格,除非热更新脚本,不太可能做到端内大版本包的更新.然而安卓端则没有此限制.因此可以做到不跳到网页或应用商店,就覆盖更新apk包. ...

  3. .NET 基础 一步步 一幕幕[面向对象之构造函数、析构函数]

    构造函数.析构函数 构造函数: 语法: //无参的构造函数 [访问修饰符] 函数名() :函数名必须与类名相同. //有参的构造函数 [访问修饰符] 函数名(参数列表):函数名必须与类名相同. 作用: ...

  4. fiddler发送post请求

    1.指定为 post 请求,输入 url Content-Type: application/x-www-form-urlencoded;charset=utf-8 request body中的参数格 ...

  5. 【详细教程】论android studio中如何申请百度地图新版Key中SHA1值

    一.写在前面 现在越来越多的API接口要求都要求提供我们的项目SHA1值,开发版目前还要求不高,但是发布版是必定要求的.而目前定位在各大APP中也较为常见,当下主流的百度地图和高德地图都在申请的时候会 ...

  6. [原]Cachedb 网络模块文档

    Cachedb 网络模块文档 整体结构 多路复用 (epoll 模块) 事件驱动 (事件封装) 缓冲管理 (上层buffer管理) 设计思想 层次化的设计,每一个模块只调用上一个模块的接口,并将耦合聚 ...

  7. JQuery的基础和应用

    <参考文档>   1.什么是?    DOM的作用:提供了一种动态的操作HTML元素的方法.    jQuery是一个优秀的js库.用来操作HTML元素的工具.    jQuery和DOM ...

  8. 分享两个BPM配置小技巧

    1.小技巧 流程图修改后发布的话版本号会+1,修改次数多了之后可能会导致版本号很高,这个时候可以将流程导出,然后删除对应的流程包再导入,发布数据模型和流程图之后,版本清零 2.小技巧 有的同事入职后使 ...

  9. android绘制圆形图片的两种方式

    看下效果先 下面有完整的示例代码 使用BitmapShader(着色器) 我们在绘制view 的时候 就是小学上美术课 用水彩笔在本子上画画 使用着色器绘制圆形图片最简单的理解方式 就是把bitmap ...

  10. Android之网络数据存储

    一.网络保存数据介绍 可以使用网络来保存数据,在需要的时候从网络上获取数据,进而显示在App中. 用网络保存数据的方法有很多种,对于不同的网络数据采用不同的上传与获取方法. 本文利用LeanCloud ...