Ucos实现多任务的基础包括几个方面:任务控制块,任务堆栈,中断,任务优先级,一一说起

首先,任务控制块的结构如下

//系统在运行一个任务的时候,按照任务的优先级获取任务控制块,再在任务堆栈中获得任务代码指针

typedef struct os_tcb {//任务控制块

OS_STK          *OSTCBStkPtr;           /*指向任务堆栈栈顶的指针*/

#if OS_TASK_CREATE_EXT_EN > 0u

void            *OSTCBExtPtr;           /*指向任务控制块扩展的指针    */

OS_STK          *OSTCBStkBottom;        /*指向任务堆栈栈底的指针     */

INT32U           OSTCBStkSize;          /*任务堆栈的长度     */

INT16U           OSTCBOpt;              /*创建任务时的选择项 */

INT16U           OSTCBId;               /*目前该域未使用      */

#endif

struct os_tcb   *OSTCBNext;             /*指向后一个任务控制块的指针 */

struct os_tcb   *OSTCBPrev;             /*指向前一个任务控制块的指针 */

#if (OS_EVENT_EN)

OS_EVENT        *OSTCBEventPtr;         /* */

#endif

#if (OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u)

OS_EVENT       **OSTCBEventMultiPtr;    /*指向事件控制块的指针   */

#endif

#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)

void            *OSTCBMsg;              /* 指向传递给任务消息的指针      */

#endif

#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)

#if OS_TASK_DEL_EN > 0u

OS_FLAG_NODE    *OSTCBFlagNode;         /* 指向事件标志节点的指针 */

#endif

OS_FLAGS         OSTCBFlagsRdy;         /*事件标志设置使得任务准备执行*/

#endif

INT32U           OSTCBDly;              /*任务等待的节拍数  */

INT8U            OSTCBStat;             /*任务当前的状态标志 */

//任务状态有以下列表

/******************************************

OS_STAT_RDY        状态就绪

OS_STAT_SEM       等待信号量

OS_STAT_MBOX    等待消息邮箱

OS_STAT_Q            等待消息队列

OS_STAT_SUSPEND 任务挂起

OS_STAT_MUTEX   等待互斥信号量

OS_STAT_FLAG      等待标志

OS_STAT_MULTI    等待多那个啥,还不知道

************************************************/

INT8U            OSTCBPrio;             /*任务的优先级别 */

INT8U            OSTCBX;                /*用于快速访问就绪表的数据 */

INT8U            OSTCBY;                /*快速访问就绪表的数据  */

OS_PRIO          OSTCBBitX;             /*快速访问就绪表的数据 */

OS_PRIO          OSTCBBitY;             /*快速访问就绪表的数据 */

#if OS_TASK_DEL_EN > 0u

INT8U            OSTCBDelReq;           /*请求删除任务时的标志        */

#endif

#if OS_TASK_PROFILE_EN > 0u//这个是用来监控任务执行状态的

INT32U           OSTCBCtxSwCtr;         /*任务呗切换到的次数  */

INT32U           OSTCBCyclesTot;        /*任务一共运行的节拍数  */

INT32U           OSTCBCyclesStart;      /* 任务开始的时候的时钟周期的快*/

OS_STK          *OSTCBStkBase;          /*任务堆栈的开始位置   */

INT32U           OSTCBStkUsed;          /* 已经使用的堆栈数量   */

#endif

#if OS_TASK_NAME_EN > 0u

INT8U           *OSTCBTaskName;//任务tcb的名称字符串指针

#endif

#if OS_TASK_REG_TBL_SIZE > 0u

INT32U           OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];//这里应该是快速保存任务寄存器的数据

#endif

} OS_TCB;

该结构体中有比较多的靠宏定义打开的变量,暂时不讨论,主要有这几个比较重要

OS_STK          *OSTCBStkPtr;           /*指向任务堆栈栈顶的指针*/

struct os_tcb   *OSTCBNext;             /*指向后一个任务控制块的指针 */

struct os_tcb   *OSTCBPrev;             /*指向前一个任务控制块的指针 */

INT8U            OSTCBPrio;             /*任务的优先级别 */

为什么没有任务代码的指针,这是因为任务代码被操作系统存放在了堆栈区中,我们知道,对于任务来说,任务代码指针实际上指的是执行这段代码的时候,处理器的PC指针,当系统中断到来的时候,这个指针自动保存,执行完中断自动恢复,执行原来的流程,ucos采用的原理就是设计一个系统级别的中断,定时的发生该中断,将执行完中断并自动恢复的过程修改成为任务切换过程,这个切换过程将我们自己的任务堆栈恢复到cpu实际的堆栈中,自动就能切换到新任务了从而实现多任务.可见,任务代码指正是应该要放在堆栈中的,后面在针对代码说.

另外,操作系统的任务控制块并不是动态申请的,而是编译的时候就已经确定有多少个的了,如下

OS_EXT  OS_TCB            OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS];   /* Table of TCBs */

OS_MAX_TASKS和OS_N_SYS_TASKS是靠os_cfg文件来定义的,前一个指的是系统最大任务数量,后一个是系统保留任务数量,系统最多只有这么些任务控制块,所以最多最多这么多任务了,同时还有一个相关联的全局变量

OS_EXT  OS_TCB           *OSTCBPrioTbl[OS_LOWEST_PRIO + 1u];

这个变量会保存系统中所有已经设置了的任务控制块的指针(也就是create了的任务),他的大小是系统最大优先级决定,ucos不允许优先级重复的原因就在这里,他会将系统的任务控制块按照优先级的形式存放在这个数组中,这样,当切换任务的时候就不需要轮询任务控制块列表,而是获取任务优先级之后立刻可以在OSTCBPrioTbl这个表中获取任务控制块,效率快很多(不仅仅是快很多的问题,实时系统要求代码运行效率是可以预估的,而轮询链表的时间是不能确定的,可能第一次就找到了,也可能到最后也找不到).

另外还有几个必备的变量

OS_EXT  OS_TCB   *OSTCBCur;

OS_EXT  OS_TCB   *OSTCBFreeList;

OS_EXT  OS_TCB   *OSTCBHighRdy;

OS_EXT  OS_TCB   *OSTCBList;

OSTCBCur标识当前着正在运行的tcb块, OSTCBFreeList系统全部tcb中空闲的tcb块链表头指针, OSTCBHighRdy当前已经准备好的最高优先级的tcb块,下一次切换的目标, OSTCBList系统有效tcb块的链表头指针

之前我们看到,在tcb变量结构中有一个next的指针和一个prev的指针,这就是用来构造链表的,在系统初始化的时候会开始构造,但是首先我们需要明白一个事情,不管链表形成了一个神马结构,实际的数据元素依然是在OSTCBTbl数组中存放的,只是程序使用的时候组织了一个链表而已

系统初始化的时候,程序调用OS_Init函数(外部编程调用),在OS_Init中调用OS_InitTCBList函数,在OS_InitTCBList中实现空链表的初始化,如下

static  void  OS_InitTCBList (void)

{

INT8U    ix;

INT8U    ix_next;

OS_TCB  *ptcb1;

OS_TCB  *ptcb2;

OS_MemClr((INT8U *)&OSTCBTbl[0],     sizeof(OSTCBTbl));

OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl));

for (ix = 0u; ix < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1u); ix++)

{

ix_next =  ix + 1u;

ptcb1   = &OSTCBTbl[ix];

ptcb2   = &OSTCBTbl[ix_next];

ptcb1->OSTCBNext = ptcb2;

#if OS_TASK_NAME_EN > 0u

ptcb1->OSTCBTaskName = (INT8U *)(void *)"?";

#endif

}

ptcb1                   = &OSTCBTbl[ix];

ptcb1->OSTCBNext        = (OS_TCB *)0;

#if OS_TASK_NAME_EN > 0u

ptcb1->OSTCBTaskName    = (INT8U *)(void *)"?";

#endif

OSTCBList               = (OS_TCB *)0;                         OSTCBFreeList           = &OSTCBTbl[0];                }

可以看到,进入这个函数,通过一次循环,将OSTCBTbl中的全部元素连接成了一个大的链表,链表的头为OSTCBFreeList,这样就完成了空任务块链表的初始化,之后OSTCBList赋值为null,等待之后的任务创建

创建任务的时候调用的函数为OSTaskCreate,我们分析其部分结构

首先,他会经过一番判定,第一,不能在中断中创建任务 第二,任务优先级不能重复

if (OSTCBPrioTbl[prio] == (OS_TCB *)0)

前面我们说过,任务创建之后,系统会根据任务的优先级将其tcb控制块放到OSTCBPrioTbl中对应的位置,那么该位置的数据就会不为0,此时要是检测到新任务创建的优先级对应的tcb已经有数据了,说明优先级重复,不能创建

之后会调用两个函数

OSTaskStkInit(task, p_arg, ptos, 0u);

OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0, 0u);

第一个函数是我们在os_cpu_c.c中需要移植的函数,他和处理器架构相关,具体查看移植指南,有一个重点是在OSTaskStkInit中的这一句

*(stk)    = (INT32U)0x01000000L;             /* xPSRxPSR T 位(第 24 位)置 1 ,否则第一次执行任务时Fault*/

*(--stk)  = (INT32U)task;                    /* Entry PointPC 肯定得指向任务入口*/

将任务的指针存放在了堆栈中,和之前说的吻合

堆栈区设置好了之后就要初始化tcb控制块,这一段代码比较长,说说几个细节

首先,系统应该从空闲控制块中取出一个控制块并且加入有效tcb控制块中,如下

ptcb = OSTCBFreeList

if (ptcb != (OS_TCB *)0)

{

OSTCBFreeList            = ptcb->OSTCBNext;

可见,空链表的表头被换成了第二个节点,表头用来进行后面的操作,也就是用来当成当前任务的任务控制块

ptcb->OSTCBStkPtr        = ptos;

ptcb->OSTCBPrio          = prio;

ptcb->OSTCBStat          = OS_STAT_RDY;

ptcb->OSTCBStatPend      = OS_STAT_PEND_OK;

ptcb->OSTCBDly           = 0u;

保存了传入的堆栈的栈顶指针,任务优先级,栈中有任务代码指针,齐活,在完成之后一些关于信号量等等的操作之后(后面说),来到了这一段

OSTCBPrioTbl[prio] = ptcb;

ptcb->OSTCBNext    = OSTCBList;

ptcb->OSTCBPrev    = (OS_TCB *)0;

if (OSTCBList != (OS_TCB *)0)

{

OSTCBList->OSTCBPrev = ptcb;

}

OSTCBList               = ptcb;

将当前创建的任务的控制块指针按照优先级放到了OSTCBPrioTbl中,后面直接可以根据优先级取出来,新的任务控制块连接到了系统已经存在的任务控制块的头部,从而完成了一个空闲控制块到有效控制块的转移,并创建了任务,等待后期调用

另外,ucos定义了一个空闲任务,用于在没有任务的情况下执行该任务,所以这个任务必须要是最低优先级,否则他会抢占别的优先级的任务,任务函数名

OS_TaskIdle

该任务什么事情都不干,在那类似空转运行,被OS_InitTaskIdle调用,而OS_InitTaskIdle被os_init调用,从而在初始化系统的时候该任务自动创建

除了空闲任务之外,还定义了一个统计任务,专用于统计系统执行情况,如cpu使用率这些,要监控系统的话,可以再统计任务钩子函数中做操作将系统运行情况输出,与之相关的变量为OSCPUUsage

ucos任务控制块详解的更多相关文章

  1. stm32位操作详解

    stm32位操作详解 STM32位操作原理 思想:把一个比特分成32位,每位都分配一个地址,这样就有32个地址,通过地址直接访问. 位操作基础 位运算 位运算的运算分量只能是整型或字符型数据,位运算把 ...

  2. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  3. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  4. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  5. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  6. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  7. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  8. Git初探--笔记整理和Git命令详解

    几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...

  9. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

随机推荐

  1. RatingBar

    题记:保持旺盛的求知欲.希望会一直这样. 说明:来了新控件了.就是经常用的打分的那种东东. 说明:1.看上图分别是系统自带的和自己做的.rating就是设置小星星的数目. 2.用系统自带的必须是Wra ...

  2. 10款免费Bootstrap后台模板演示及下载

    自从有了类似Bootstrap这样强大的前端框架之后,无论我们是做静态页面,还是做网站主题,着实方便很多.即便有很多类似的其他国产.海外的前端框架比较,Bootstrap用户量以及功能文档还是比较大的 ...

  3. javascript语句语义大全(2)

    1. 四则运算相关 +,-,*,/,% 分别是加减乘除和取余 2.Math.pow(a,b) a的b次方 3.toFixed(a) 四舍五入为指定小数位数的数字 4. k++; ++K 看似相同但是在 ...

  4. 5--OC--构造方法

    //  Created by Stephen on 16/3/2.//  Copyright © 2016年 Stephen. All rights reserved.//// 回顾上一章节Perso ...

  5. redis 工具类 单个redis、JedisPool 及多个redis、shardedJedisPool与spring的集成配置

    http://www.cnblogs.com/edisonfeng/p/3571870.html http://javacrazyer.iteye.com/blog/1840161 http://ww ...

  6. JS的数据类型转换

    JS 数据类型转换 方法主要有三种 转换函数.强制类型转换.利用js变量弱类型转换. 1. 转换函数: js提供了parseInt()和parseFloat()两个转换函数.前者把值转换成整数,后者把 ...

  7. 【转】你必须了解的Session的本质

    有一点我们必须承认,大多数web应用程序都离不开session的使用.这篇文章将会结合php以及http协议来分析如何建立一个安全的会话管理机制.我们先简单的了解一些http的知识,从而理解该协议的无 ...

  8. Linux 挂载aliyun数据盘

    适用系统:Linux(Redhat , CentOS,Debian,Ubuntu) *  Linux的云服务器数据盘未做分区和格式化,可以根据以下步骤进行分区以及格式化操作. 下面的操作将会把数据盘划 ...

  9. Linux的环境变量

     一.Linux的变量种类 按变量的生存周期来划分,Linux变量可分为两类: 1.永久的:需要修改配置文件,变量永久生效. 2.临时的:使用export命令声明即可,变量在关闭shell时失效. 二 ...

  10. 一种快速查询多点DS18B20温度的方法(转)

    源:http://hi.baidu.com/james_xiao/item/79b961c90623093e45941623 一种快速查询多点DS18B20温度的方法 引言      为了满足实时性要 ...