在使用rtos时需要给任务分配合适大小的任务栈,任务运行时所占用的任务栈大小由整个任务所使用的临时变量多少决定,当任务不复杂且调用子函数不多时,可以通过简单的计算来判断需要给任务分配多大的任务栈。当任务比较复杂,调用函数比较多时,计算起来比较麻烦。TencentOS Tiny提供了API可以检测任务最多使用了任务栈多少字节,在使用最多字节的基础上多分配一部分空间,就是一个合适的任务栈大小。
TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN 1U
int depth; tos_task_stack_draught_depth(&task1, &depth); printf("%d", depth);
在tos_task_creat()
中使用了cpu_task_stk_init()
函数对任务栈进行处理
__API__ k_err_t tos_task_create(k_task_t *task,char *name, k_task_entry_t entry, void *arg, k_prio_t prio, k_stack_t *stk_base, size_t stk_size, k_timeslice_t timeslice) { ....... ....... /* task_exist是提前声明的私有函数,用于销毁任务 * __STATIC__ void task_exit(void) * { * tos_task_destroy(K_NULL); * } */ task->sp = cpu_task_stk_init((void *)entry, arg, (void *)task_exit, stk_base, stk_size); task->entry = entry; task->arg = arg; task->prio = prio; task->stk_base = stk_base; task->stk_size = stk_size; strncpy(task->name, name, K_TASK_NAME_LEN_MAX); ...... ...... return K_ERR_NONE; }
cpu_task_stk_init()
函数在使能了TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN
后会给任务栈的所有字节初始化为0xCC
,并将寄存器压栈
__KNL__ k_stack_t *cpu_task_stk_init(void *entry, void *arg, void *exit, k_stack_t *stk_base, size_t stk_size) { cpu_data_t *sp; // 栈顶做4字节对齐 sp = (cpu_data_t *)&stk_base[stk_size]; sp = (cpu_data_t *)((cpu_addr_t)sp & 0xFFFFFFF8); #if TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN > 0u // 从栈底到栈顶所有字节初始化为0xCC uint8_t *slot = (uint8_t *)&stk_base[0]; for (; slot < (uint8_t *)sp; ++slot) { *slot = 0xCC; } #endif // 按顺序将寄存器压入任务栈 /* auto-saved on exception(pendSV) by hardware */ *--sp = (cpu_data_t)0x01000000u; /* xPSR */ *--sp = (cpu_data_t)entry; /* entry */ *--sp = (cpu_data_t)exit; /* R14 (LR) */ *--sp = (cpu_data_t)0x12121212u; /* R12 */ *--sp = (cpu_data_t)0x03030303u; /* R3 */ *--sp = (cpu_data_t)0x02020202u; /* R2 */ *--sp = (cpu_data_t)0x01010101u; /* R1 */ *--sp = (cpu_data_t)arg; /* R0: arg */ /* Remaining registers saved on process stack */ /* EXC_RETURN = 0xFFFFFFFDL Initial state: Thread mode + non-floating-point state + PSP 31 - 28 : EXC_RETURN flag, 0xF 27 - 5 : reserved, 0xFFFFFE 4 : 1, basic stack frame; 0, extended stack frame 3 : 1, return to Thread mode; 0, return to Handler mode 2 : 1, return to PSP; 0, return to MSP 1 : reserved, 0 0 : reserved, 1 */ #if defined (TOS_CFG_CPU_ARM_FPU_EN) && (TOS_CFG_CPU_ARM_FPU_EN == 1U) *--sp = (cpu_data_t)0xFFFFFFFDL; #endif *--sp = (cpu_data_t)0x11111111u; /* R11 */ *--sp = (cpu_data_t)0x10101010u; /* R10 */ *--sp = (cpu_data_t)0x09090909u; /* R9 */ *--sp = (cpu_data_t)0x08080808u; /* R8 */ *--sp = (cpu_data_t)0x07070707u; /* R7 */ *--sp = (cpu_data_t)0x06060606u; /* R6 */ *--sp = (cpu_data_t)0x05050505u; /* R5 */ *--sp = (cpu_data_t)0x04040404u; /* R4 */ return (k_stack_t *)sp; }
在 tos_task.c
文件中提供了 API:tos_task_stack_draught_depth(k_task_t *task, int *depth)
__API__ k_err_t tos_task_stack_draught_depth(k_task_t *task, int *depth) { TOS_CPU_CPSR_ALLOC(); k_err_t rc; // 判断传入的参数depth是否合法,不合法直接return TOS_PTR_SANITY_CHECK(depth); // 判断传入的参数task是否合法,不合法则使用当前的运行的任务task if (unlikely(!task)) { task = k_curr_task; } TOS_OBJ_VERIFY(task, KNL_OBJ_TYPE_TASK); // 关中断 TOS_CPU_INT_DISABLE(); // 实际用于检测任务栈最大深度的函数 rc = cpu_task_stack_draught_depth(task->stk_base, task->stk_size, depth); // 开中断 TOS_CPU_INT_ENABLE(); return rc; }
可以看到实际检测任务栈最大深度的函数是 cpu_task_stack_draught_depth(k_stack_t *stk_base, size_t stk_size, int *depth)
__KNL__ k_err_t cpu_task_stack_draught_depth(k_stack_t *stk_base, size_t stk_size, int *depth) { uint8_t *slot; uint8_t *sp, *bp; int the_depth = 0; // 栈底 bp = (uint8_t *)&stk_base[0]; // 栈顶4字节对齐 sp = &stk_base[stk_size]; sp = (uint8_t *)((cpu_addr_t)sp & 0xFFFFFFF8); /* * 从栈顶开始遍历到栈底, * 由于在初始化时将任务栈的每个字节初始化为了0xCC, * 如果当前位置的值不为0xCC则是被任务使用了, * 栈顶的地址与当前位置的地址的差值为此时任务使用的深度, * 当遍历到最后一个不为0xCC的地址时, * 差值为任务栈使用的最大深度 */ for (slot = sp - 1; slot >= bp; --slot) { if (*slot != 0xCC) { the_depth = sp - slot; } } *depth = the_depth; if (the_depth == stk_size) { return K_ERR_TASK_STK_OVERFLOW; } return K_ERR_NONE; }