ucosii任务三要素---执行代码 堆栈 任务控制块
使用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任务三要素---执行代码 堆栈 任务控制块相关推荐
- HTTP.sys 远程执行代码验证工具
漏洞信息: 远程执行代码漏洞存在于 HTTP 协议堆栈 (HTTP.sys) 中,当 HTTP.sys 未正确分析经特殊设计的 HTTP 请求时会导致此漏洞.这里将测试工具改成windows版本方便工 ...
- javascript之执行上下文堆栈
执行上下文堆栈 有三种类型的ECMAScript代码:全局代码,函数代码和eval代码.代码执行在它的执行上下文里. 有唯一的全局上下文,以及可能有多个函数和eval上下文.每一个函数调用,进入到函数 ...
- 找不到libmmd.dll无法继续执行代码_300 行代码带你秒懂 Java 多线程!| 原力计划...
作者 | 永远在路上[] 责编 | 胡巍巍 出品 | CSDN博客 线程线程的概念,百度是这样解释的:线程(英语:Thread)是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际 ...
- CVE-2015-1635-HTTP.SYS远程执行代码漏洞复现
CVE-2015-1635-HTTP.SYS远程执行代码漏洞复现 一.漏洞描述 远程执行代码漏洞存在于 HTTP 协议堆栈 (HTTP.sys) 中,当 HTTP.sys 未正确分析经特殊设计的 HT ...
- Log4j2远程执行代码漏洞如何攻击? 又如何修复
Log4j2远程执行代码漏洞如何攻击? 又如何修复 12月9日晚,Apache Log4j2反序列化远程代码执行漏洞细节已被公开,Apache Log4j-2中存在JNDI注入漏洞,当程序将用户输入的 ...
- 可执行代码和命令执行
代码执行 代码执行(Executable Code) 是指将目标代码连接后形成的代码,简单来说是机器能够直接执行的代码,可执行代码当然也是二进制的.源代码(也称源程序)是指未编译的按照一定的程序设计语 ...
- 大智慧L2实时api接口的逐笔委托功能执行代码分享
用过l2行情接口的投资者应该都了解大部分的接口都是有逐笔委托功能的,那么大智慧L2实时api接口也有着很强大的逐笔委托功能,投资者可以通过这个逐笔委托功能可以看到一个股票的每天出现的全部委托指令,那么 ...
- 如何用C#动态编译、执行代码
在开始之前,先熟悉几个类及部分属性.方法:CSharpCodeProvider.ICodeCompiler.CompilerParameters.CompilerResults.Assembly. 一 ...
- flask执行python程序_Flask app后如何执行代码(应用程序运行)开始
但我想使用一种方法,它还可以保存相机中的所有相框(我已经有功能了).在 问题是,一旦我启动了Flask应用程序,我最多只能存储在localhost中打开web页面时捕获的帧.我希望能够在应用程序运行时 ...
最新文章
- 布隆过滤器+布隆过滤器(Bloom Filter)详解
- boyer moore算法 java_Boyer-Moore算法
- 戴维南定律和诺顿定律
- 如何全面的把握一个系统的异常处理
- skysat重访周期_重访小恶梦
- 使用nagios监控io,内存
- jquery用ajax,超简单的jquery的AJAX用法
- Mysql锁定表/解锁句法
- 小爱音箱mini系统故障怎么办_Win7蓝牙连接小爱音箱mini的详细教程
- php开发bt种子,[原创作品][PHP]BT种子打包推送小神器~~
- 浅谈Struts2拦截器的原理与实现
- Radix Tree总结
- vue-cli从2升级到3报错error 404 Not Found: @wry/context@^0.4.0
- 浙大翁恺pat练习题_中国大学MOOC-翁恺-C语言-PAT习题及解答-第二周
- 360 一个让人又爱又恨的公司
- BF、KMP、BM、Sunday算法讲解
- php简单后台,ThinkPHP简单网站后台
- Apache Echarts常用图表之柱状图
- 分花生游戏 (博弈论)
- MySQL 8.0 Command Line Client 不能打开或者闪退