uCOSIII学习笔记之任务堆栈的初始化

来源:互联网 发布:广州数据库开发工程师 编辑:程序博客网 时间:2024/05/22 09:06
  • 任务栈的初始化在任务创建的时候完成,由函数 OS_Task_Create() 或者 OSTaskCreateExt() 调用函数 OSTaskStkInit() 来完成。任务栈的初始化过程跟具体的CPU有很大的关联。 (本文的所参考的初始化过程基于Cortex-M3内核)

  • 栈有四种类型:


如果有表达式 a = i++ 它等价于 a = i ; i = i + 1;(先使用值,后增加)
如果有表达式 a = ++i 它等价于 i = i + 1; a = i;(先自加,后使用值)

满增栈

PUSH R0    ;*(++SP) = R0POP  R0    ;R0 = *(SP--)

满减栈

PUSH R0    ;*(--SP) = R0POP  R0    ;R0 = *(SP++)

空增栈

PUSH R0    ;*(SP++) = R0POP  R0    ;R0 = *(--SP)

空减栈

PUSH R0    ;*(SP--) = R0POP  R0    ;R0 = *(++SP)

在ARM中,使用的是满减栈!

  • 在OS_Task_Create() 函数执行的时候,我们传入了三个关于栈的参数,分别是:
CPU_STK       *p_stk_baseCPU_STK_SIZE   stk_limitCPU_STK_SIZE   stk_size

第一个参数是栈的基地址,基地址总是栈空间的低地址;第二个参数是任务栈的深度标记,该参数的数值代表了栈溢出警告之前栈内应该剩余的空间大小;第三个参数是实际分配的任务栈的大小。

#if (CPU_CFG_STK_GROWTH == CPU_STK_GROWTH_HI_TO_LO)    p_stk_limit = p_stk_base + stk_limit;#else    p_stk_limit = p_stk_base + (stk_size - 1u) - stk_limit;#endif

通过在cpu.h这个文件中的宏定义设置堆栈的增长方向,根据不同的增长方向,给p_stk_limit设置相应的值,当栈指针到达这个地址时,说明栈的使用已经达到了预警值,再继续使用栈会有栈溢出的危险。在调用函数OS_Task_Create()时,我们给出了stk_limit和stk_size两个参数,正常使用的情况下,stk_limit < stk_size , stk_limit 的值越大说明栈的使用达到预警值时,栈空间实际留有的裕量越多。例如:stk_limit = stk_size/10 , 则当栈空间使用超过90%时,就达到了栈空间的极限深度。

在函数OSTaskStkInit()中,栈的初始化过程如下:

CPU_STK  *OSTaskStkInit (OS_TASK_PTR    p_task,                         void          *p_arg,                         CPU_STK       *p_stk_base,                         CPU_STK       *p_stk_limit,                         CPU_STK_SIZE   stk_size,                         OS_OPT         opt){    CPU_STK  *p_stk;    (void)opt;                               /* Prevent compiler warning */    p_stk = &p_stk_base[stk_size];           /* Load stack pointer */                                             /* Registers stacked as if auto-saved on exception */    *--p_stk = (CPU_STK)0x01000000u;         /* xPSR */    *--p_stk = (CPU_STK)p_task;              /* Entry Point */    *--p_stk = (CPU_STK)OS_TaskReturn;       /* R14 (LR) */    *--p_stk = (CPU_STK)0x12121212u;         /* R12 */    *--p_stk = (CPU_STK)0x03030303u;         /* R3 */    *--p_stk = (CPU_STK)0x02020202u;         /* R2 */    *--p_stk = (CPU_STK)p_stk_limit;         /* R1 */    *--p_stk = (CPU_STK)p_arg;               /* R0 : argument */                                             /* Remaining registers saved on process stack */    *--p_stk = (CPU_STK)0x11111111u;         /* R11 */    *--p_stk = (CPU_STK)0x10101010u;         /* R10 */    *--p_stk = (CPU_STK)0x09090909u;         /* R9 */    *--p_stk = (CPU_STK)0x08080808u;         /* R8 */    *--p_stk = (CPU_STK)0x07070707u;         /* R7 */    *--p_stk = (CPU_STK)0x06060606u;         /* R6 */    *--p_stk = (CPU_STK)0x05050505u;         /* R5 */    *--p_stk = (CPU_STK)0x04040404u;         /* R4 */    return (p_stk);}

在函数的一开始就执行了语句:

p_stk = &p_stk_base[stk_size];

栈指针最初应该指向最高地址,实际分配的任务栈的最高地址是:&p_stk_base[stk_size - 1],但由于在ARM中,使用的是满减栈,如果p_stk = &p_stk_base[stk_size - 1]; 则任务栈第一个元素并没有使用,实际执行的语句虽然貌似数组越界了,但是地址“&p_stk_base[stk_size]”从未解引用,所以并没有造成数组使用越界。

在执行异常服务程序之前,PSR返回地址LRR12R3R2R1R0这些寄存器是自动保存的,在执行PendSV异常服务进行任务切换时,不需要保存这些寄存器的值。程序初始化栈时,这些寄存器在栈中顺序与自动保存的顺序完全一样,在异常发生时,自动的保存的顺序就是PSR……R0. 初始化时,其中返回地址就是任务函数的入口地址;在任务函数开始执行时,我们传入了参数(void *p_arg),所以在创建任务,初始化堆栈时,将参数赋值给R0寄存器。剩下的R11-R4寄存器依次放入初始化栈。
返回当前的栈顶指针,至此,任务栈的初始化完成。

疑问:

 *--p_stk = (CPU_STK)p_stk_limit;         /* R1 */

执行了这句语句后,在此任务开始执行时,p_stk_limit的值将被赋值到寄存器R1,我还没弄明白这句代码的作用。

0 0
原创粉丝点击