使用ucosii也有一段时间了,把学习到的总结一下。这篇文章不是对ucosii如何使用的讲解,而是主要看看ucosii内核实现的原理,或者说讲一些RTOS种通用的知识。对于RTOS基础知识的讲解,暂时就不表述了。

RTOS中一个任务的三要素:程序代码、堆栈、任务控制块。

程序代码就是任务的执行函数。这没啥好说的

每个任务都有自己的堆栈,来保存自身的信息。这是每个任务自己的资源。是每个人自己的一亩三分地。

任务控制块则是对任务的描述,例如任务名称、任务优先级、堆栈地址等等信息。

任务如何切换呢?

首先,理解一句话叫做,处理器就是个傻瓜,PC让它干啥,它就干啥,PC指向哪儿,它就去哪儿。这句话就是任务切换的本质,你想让处理器执行任务A,好,让PC指向任务A,你想切换到任务B,没问题,让PC指向任务B就搞定了。说的正式一点,系统是通过把带运行程序的地址赋予程序计数器PC来实现任务的切换的。

任务运行时处理器与内存之间的关系

处理器通过两个指针寄存器PC和SP来与任务代码和任务堆栈建立联系,PC指向的是执行的任务代码,SP则是指向任务的堆栈。

有的学习材料将程序运行环境分为两个部分,一部分是处理器中运行环境,另一部分是内存中的运行环境,内存中的运行环境被称为虚拟运行环境。个人觉得这种说法可能会带来误解。这里也解释解释,只有将处理器的PC值和SP赋值(以及其他一些通用和状态寄存器),任务才能真正运行起来。而在内存中呢,会把各个任务的PC和SP值(以及其他一些通用和状态寄存器)保存(或者叫备份)一份,当需要运行这个任务的时候呢,就把这些值从内存中拷贝到处理器的对应寄存器中,这个任务呢就跑起来了。

有了上面这段话,任务如何切换就应该有一个大致的印象了。

左侧是处理器,中间的是内存中的空间,内存有每一个任务的信息备份。当运行某个任务的时候,就把该任务的虚拟环境从内存中放到真实的处理器环境当中。

我们还要考虑到一点,当切换任务时,当前正在执行的任务怎么办呢?很简单,将当前任务信息从处理器备份到内存中就可以了,下一次用的时候再从内存中复制到处理器中就OK了。

内存中要备份任务的什么信息呢

上面的任务切换就是备份当前任务的信息到内存中,赋值下一个任务信息到处理器中。那么问题是,需要保存那些任务信息呢?

用通俗的话说一说要保存的内容,

1、PC 。亲们,中断发生时需要做什么呢?现场保护,其实任务切换和中断的工作也有很多共同之处。假如当前正在执行i++,此时我们要进行任务切换,我们需要把PC值保存,等到下一次执行这个任务时,我们还能从i++开始执行。

2、任务堆栈指针SP ,前面我们一直在说这个东东,只有知道了sp我们才能找到对应的任务堆栈,才能找到堆栈保存任务信息。所以只要找到了任务栈指针SP,任务的私有信息就找到了。

3、PSW 程序状态字寄存器,例如一些进位、补位信息。这些不能丢,否则,在此切换到该任务时,计算不就错误了。

4、通用寄存器内容,这没啥说的,可能会保存一些变量的值,我们当然得保存起来了。

ARM在中断会自动将R0-R3 R12 LR PC xPSR保存起来,所以我们要保存是通过用寄存器中剩下的R4-R11 和SP。SP指向的任务栈,只有知道了SP,才能存储其他信息。这里还有知道一点,除了SP,其他要保存的信息都是保存在各自的任务栈中。但是SP是保存在主栈当中的。或者这样说,SP是保存在我们前面提到的任务块当中,任务块并不是存在任务栈当中的,而是存在于主栈当中的。

我们来看看任务块中都有什么

typedef struct os_tcb {OS_STK          *OSTCBStkPtr;           /* Pointer to current top of stack                         */#if OS_TASK_CREATE_EXT_EN > 0uvoid            *OSTCBExtPtr;           /* Pointer to user definable data for TCB extension        */OS_STK          *OSTCBStkBottom;        /* Pointer to bottom of stack                              */INT32U           OSTCBStkSize;          /* Size of task stack (in number of stack elements)        */INT16U           OSTCBOpt;              /* Task options as passed by OSTaskCreateExt()             */INT16U           OSTCBId;               /* Task ID (0..65535)                                      */
#endifstruct os_tcb   *OSTCBNext;             /* Pointer to next     TCB in the TCB list                 */struct os_tcb   *OSTCBPrev;             /* Pointer to previous TCB in the TCB list                 */#if (OS_EVENT_EN)OS_EVENT        *OSTCBEventPtr;         /* Pointer to          event control block                 */
#endif#if (OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u)OS_EVENT       **OSTCBEventMultiPtr;    /* Pointer to multiple event control blocks                */
#endif#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)void            *OSTCBMsg;              /* Message received from OSMboxPost() or OSQPost()         */
#endif#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
#if OS_TASK_DEL_EN > 0uOS_FLAG_NODE    *OSTCBFlagNode;         /* Pointer to event flag node                              */
#endifOS_FLAGS         OSTCBFlagsRdy;         /* Event flags that made task ready to run                 */
#endifINT32U           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          */#if OS_TASK_DEL_EN > 0uINT8U            OSTCBDelReq;           /* Indicates whether a task needs to delete itself         */
#endif#if OS_TASK_PROFILE_EN > 0uINT32U           OSTCBCtxSwCtr;         /* Number of time the task was switched in                 */INT32U           OSTCBCyclesTot;        /* Total number of clock cycles the task has been running  */INT32U           OSTCBCyclesStart;      /* Snapshot of cycle counter at start of task resumption   */OS_STK          *OSTCBStkBase;          /* Pointer to the beginning of the task stack              */INT32U           OSTCBStkUsed;          /* Number of bytes used from the stack                     */
#endif#if OS_TASK_NAME_EN > 0uINT8U           *OSTCBTaskName;
#endif#if OS_TASK_REG_TBL_SIZE > 0uINT32U           OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];
#endif
} OS_TCB;

这是任务块的原型,是不是太复杂了,我们来简化一下

typedef struct os_tcb {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            OSTCBPrio;             /* Task priority (0 == highest)                            */} OS_TCB;

简单来看,任务控制块就可以简化成上面的样子

OSTCBStkPtr:任务栈指针,放在任务块的开始,方便使用

OSTCBNext:指向下一个任务块的指针

OSTCBPrev:指向上一个任务块的指针

从这里也能看出,系统的所有任务是一个双链表结构。

OSTCBDly:延迟拍数,使用delay函数会用到这个变量

OSTCBStat:任务的状态

OSTCBPrio:任务的优先级,ucosii中不允许想同优先级的任务,所以可以通过优先级唯一的区分任务

在系统初始化的时候,会根据设定的最大任务数创建一个空任务控制块链表OSTCBFreeList

void  OSInit (void)
{OSInitHookBegin();                                           /* Call port specific initialization code   */OS_InitMisc();                                               /* Initialize miscellaneous variables       */OS_InitRdyList();                                            /* Initialize the Ready List                */OS_InitTCBList();                                            /* 初始化一个空任务控制块链表     */OS_InitEventList();                                          /* Initialize the free list of OS_EVENTs    */#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)OS_FlagInit();                                               /* Initialize the event flag structures     */
#endif#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)OS_MemInit();                                                /* Initialize the memory manager            */
#endif#if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u)OS_QInit();                                                  /* Initialize the message queue structures  */
#endifOS_InitTaskIdle();                                           /* Create the Idle Task                     */
#if OS_TASK_STAT_EN > 0uOS_InitTaskStat();                                           /* Create the Statistic Task                */
#endif#if OS_TMR_EN > 0uOSTmr_Init();                                                /* Initialize the Timer Manager             */
#endifOSInitHookEnd();                                             /* Call port specific init. code            */#if OS_DEBUG_EN > 0uOSDebugInit();
#endif
}
static  void  OS_InitTCBList (void)
{INT8U    ix;INT8U    ix_next;OS_TCB  *ptcb1;OS_TCB  *ptcb2;OS_MemClr((INT8U *)&OSTCBTbl[0],     sizeof(OSTCBTbl));      /* Clear all the TCBs                 */OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl));  /* Clear the priority table           */for (ix = 0u; ix < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1u); ix++) {    /* Init. list of free TCBs    根据最大任务数创建空任务链表 */ix_next =  ix + 1u;ptcb1   = &OSTCBTbl[ix];ptcb2   = &OSTCBTbl[ix_next];ptcb1->OSTCBNext = ptcb2;
#if OS_TASK_NAME_EN > 0uptcb1->OSTCBTaskName = (INT8U *)(void *)"?";             /* Unknown name                       */
#endif}ptcb1                   = &OSTCBTbl[ix];ptcb1->OSTCBNext        = (OS_TCB *)0;                       /* Last OS_TCB                        */
#if OS_TASK_NAME_EN > 0uptcb1->OSTCBTaskName    = (INT8U *)(void *)"?";              /* Unknown name                       */
#endifOSTCBList               = (OS_TCB *)0;                       /* TCB lists initializations          */OSTCBFreeList           = &OSTCBTbl[0];
}

创建完毕如下图,形成一个空任务链表,在创建任务时,就从空任务链表中拿一个任务控制块放到任务控制链表OSTCBList中

如何对任务进行索引呢?ucosii把所有的任务块指针按照优先级放到一个指针数组OSTCBPrioTbl[OS_LOWEST_PRIO + 1u]中,通过这个指针数组就能找到每一个任务块的地址,创建任务完成后,形成如下图所示的数据结构图

当应用程序调用OSTaskCreate()函数时创建一个任务时,这个函数会调用调用系统函数OSTcbInit()来为任务控制块进行初始化。这个函数首先为被创建的任务控制块从空任务控制链表中获取一个任务控制块,然后对任务的属性对任务控制块各个成员进行赋值,最后再把这个任务控制块插入任务控制链表OSTCBList的头部,使用链表头插的方法。至此,任务就创建完成了。

/*
*********************************************************************************************************
*                                            INITIALIZE TCB
*
* Description: This function is internal to uC/OS-II and is used to initialize a Task Control Block when
*              a task is created (see OSTaskCreate() and OSTaskCreateExt()).
*
* Arguments  : prio          is the priority of the task being created
*
*              ptos          is a pointer to the task's top-of-stack assuming that the CPU registers
*                            have been placed on the stack.  Note that the top-of-stack corresponds to a
*                            'high' memory location is OS_STK_GROWTH is set to 1 and a 'low' memory
*                            location if OS_STK_GROWTH is set to 0.  Note that stack growth is CPU
*                            specific.
*
*              pbos          is a pointer to the bottom of stack.  A NULL pointer is passed if called by
*                            'OSTaskCreate()'.
*
*              id            is the task's ID (0..65535)
*
*              stk_size      is the size of the stack (in 'stack units').  If the stack units are INT8Us
*                            then, 'stk_size' contains the number of bytes for the stack.  If the stack
*                            units are INT32Us then, the stack contains '4 * stk_size' bytes.  The stack
*                            units are established by the #define constant OS_STK which is CPU
*                            specific.  'stk_size' is 0 if called by 'OSTaskCreate()'.
*
*              pext          is a pointer to a user supplied memory area that is used to extend the task
*                            control block.  This allows you to store the contents of floating-point
*                            registers, MMU registers or anything else you could find useful during a
*                            context switch.  You can even assign a name to each task and store this name
*                            in this TCB extension.  A NULL pointer is passed if called by OSTaskCreate().
*
*              opt           options as passed to 'OSTaskCreateExt()' or,
*                            0 if called from 'OSTaskCreate()'.
*
* Returns    : OS_ERR_NONE         if the call was successful
*              OS_ERR_TASK_NO_MORE_TCB  if there are no more free TCBs to be allocated and thus, the task cannot
*                                  be created.
*
* Note       : This function is INTERNAL to uC/OS-II and your application should not call it.
*********************************************************************************************************
*/INT8U  OS_TCBInit (INT8U    prio,OS_STK  *ptos,OS_STK  *pbos,INT16U   id,INT32U   stk_size,void    *pext,INT16U   opt)
{OS_TCB    *ptcb;
#if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */OS_CPU_SR  cpu_sr = 0u;
#endif
#if OS_TASK_REG_TBL_SIZE > 0uINT8U      i;
#endifOS_ENTER_CRITICAL();ptcb = OSTCBFreeList;                                  /* Get a free TCB from the free TCB list 从空任务链表中获取一个任务控制块   */if (ptcb != (OS_TCB *)0) {OSTCBFreeList            = ptcb->OSTCBNext;        /* Update pointer to free TCB list          */OS_EXIT_CRITICAL();                                /*根据参数对任务块各个成员赋值*/ptcb->OSTCBStkPtr        = ptos;                   /* Load Stack pointer in TCB                */ptcb->OSTCBPrio          = prio;                   /* Load task priority into TCB              */ptcb->OSTCBStat          = OS_STAT_RDY;            /* Task is ready to run                     */ptcb->OSTCBStatPend      = OS_STAT_PEND_OK;        /* Clear pend status                        */ptcb->OSTCBDly           = 0u;                     /* Task is not delayed                      */
............OSTCBInitHook(ptcb);OSTaskCreateHook(ptcb);                            /* Call user defined hook                   */OS_ENTER_CRITICAL();OSTCBPrioTbl[prio] = ptcb;                         /*将这个任务控制块地址放到OSTCBPrioTbl数组中*/ptcb->OSTCBNext    = OSTCBList;                    /* Link into TCB chain                      */ptcb->OSTCBPrev    = (OS_TCB *)0;                  /*将这个任务控制块插到任务控制块链表的头部*/if (OSTCBList != (OS_TCB *)0) {OSTCBList->OSTCBPrev = ptcb;}OSTCBList               = ptcb;OSRdyGrp               |= ptcb->OSTCBBitY;         /* Make task ready to run                   */OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;OSTaskCtr++;                                       /* Increment the #tasks counter             */OS_EXIT_CRITICAL();return (OS_ERR_NONE);}OS_EXIT_CRITICAL();return (OS_ERR_TASK_NO_MORE_TCB);
}

ucosii任务三要素---执行代码 堆栈 任务控制块相关推荐

  1. HTTP.sys 远程执行代码验证工具

    漏洞信息: 远程执行代码漏洞存在于 HTTP 协议堆栈 (HTTP.sys) 中,当 HTTP.sys 未正确分析经特殊设计的 HTTP 请求时会导致此漏洞.这里将测试工具改成windows版本方便工 ...

  2. javascript之执行上下文堆栈

    执行上下文堆栈 有三种类型的ECMAScript代码:全局代码,函数代码和eval代码.代码执行在它的执行上下文里. 有唯一的全局上下文,以及可能有多个函数和eval上下文.每一个函数调用,进入到函数 ...

  3. 找不到libmmd.dll无法继续执行代码_300 行代码带你秒懂 Java 多线程!| 原力计划...

    作者 | 永远在路上[] 责编 | 胡巍巍 出品 | CSDN博客 线程线程的概念,百度是这样解释的:线程(英语:Thread)是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际 ...

  4. CVE-2015-1635-HTTP.SYS远程执行代码漏洞复现

    CVE-2015-1635-HTTP.SYS远程执行代码漏洞复现 一.漏洞描述 远程执行代码漏洞存在于 HTTP 协议堆栈 (HTTP.sys) 中,当 HTTP.sys 未正确分析经特殊设计的 HT ...

  5. Log4j2远程执行代码漏洞如何攻击? 又如何修复

    Log4j2远程执行代码漏洞如何攻击? 又如何修复 12月9日晚,Apache Log4j2反序列化远程代码执行漏洞细节已被公开,Apache Log4j-2中存在JNDI注入漏洞,当程序将用户输入的 ...

  6. 可执行代码和命令执行

    代码执行 代码执行(Executable Code) 是指将目标代码连接后形成的代码,简单来说是机器能够直接执行的代码,可执行代码当然也是二进制的.源代码(也称源程序)是指未编译的按照一定的程序设计语 ...

  7. 大智慧L2实时api接口的逐笔委托功能执行代码分享

    用过l2行情接口的投资者应该都了解大部分的接口都是有逐笔委托功能的,那么大智慧L2实时api接口也有着很强大的逐笔委托功能,投资者可以通过这个逐笔委托功能可以看到一个股票的每天出现的全部委托指令,那么 ...

  8. 如何用C#动态编译、执行代码

    在开始之前,先熟悉几个类及部分属性.方法:CSharpCodeProvider.ICodeCompiler.CompilerParameters.CompilerResults.Assembly. 一 ...

  9. flask执行python程序_Flask app后如何执行代码(应用程序运行)开始

    但我想使用一种方法,它还可以保存相机中的所有相框(我已经有功能了).在 问题是,一旦我启动了Flask应用程序,我最多只能存储在localhost中打开web页面时捕获的帧.我希望能够在应用程序运行时 ...

最新文章

  1. 简单又实用的分享!SharePoint母版页引用(实战)
  2. linux硬件配置软件,洞悉Linux系统软硬件配置
  3. Android中利用Jsoup让WebView清除Html标签并让图片适应大小并居中
  4. android自定义图片文本,Android 实现文字与图片的混排
  5. 静态初始化块的执行顺序
  6. 【NLP】听李宏毅点评GPT-3:来自猎人暗黑大陆的模型
  7. 交互 点击变色_明年旗舰机必将标配!电致变色技术解读
  8. VHDL-std_logic_vector转换为integer注意
  9. extjs 基础部分
  10. Magento事件与事件监听
  11. vue2.0 自定义 生成二维码(QRCode)组件
  12. sqlserver 没有维护计划_设定数据库备份计划,安全放心不怕事!
  13. babel-preset-env
  14. linux更新系统内核,Linux内核升级方法详解
  15. 智慧城市大数据可视化系统设计心得
  16. delphi xe 10 程式外观
  17. 红旗河工程,南水北调西线工程,藏水入疆工程三合一
  18. nodejs android ios,NodeJs 搭建WebSocket Push Server Web Android iOS Client
  19. Windows下Goland的Debug显示“frames are not available“
  20. 2023年华为认证HCIA云计算题库(H13-511)

热门文章

  1. “X-XSS-Protection”头缺失或不安全
  2. mysql find next_GitHub - flymysql/hexo-theme-next-flyme: 自己的hexo博客在next主题上做的修改...
  3. MATLAB--数字图像处理 计算图像链码及其相似多边形
  4. cad流程图怎么画文丘里_用CAD绘制交互流程图的方法步骤
  5. linux-磁盘空间满了,找不到占用文件
  6. 面试系列一:简历之 简历包含哪些内容
  7. 1.7-26:字符串最大跨距
  8. mpp动态新增和删除分区的定时任务创建
  9. PHP 日期格式化(获取上月第一天、最后一天等)
  10. 不用代码,10分钟会采集微博、微信、知乎、58同城数据和信息