TencentOS_Tiny 任务栈使用率(检测任务栈最大使用深度)


目录

TencentOS_Tiny 任务栈使用率

在使用rtos时需要给任务分配合适大小的任务栈,任务运行时所占用的任务栈大小由整个任务所使用的临时变量多少决定,当任务不复杂且调用子函数不多时,可以通过简单的计算来判断需要给任务分配多大的任务栈。当任务比较复杂,调用函数比较多时,计算起来比较麻烦。TencentOS Tiny提供了API可以检测任务最多使用了任务栈多少字节,在使用最多字节的基础上多分配一部分空间,就是一个合适的任务栈大小。

API调用

在CONFIG.h中使能

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;
}

原创文章,作者:3628473679,如若转载,请注明出处:https://blog.ytso.com/245031.html

(0)
上一篇 2022年4月18日
下一篇 2022年4月18日

相关推荐

发表回复

登录后才能评论