μC/OS III - 任务调度 Ⅰ:调度过程和调度点

这是μ/C OS III任务调度的第一篇文章:调度过程和调度点。基于Cortex-M系列的处理器,从最简单的创建任务开始,分析μC/OS III的任务调度过程。包括上下文切换的详细过程、任务的栈分配详情、引起调度的调度点等内容。

一. 从哪开始

先来看一段简单的代码:

#define TASK_PRIO         (10)
#define TASK_STACK_ZIZE   (128)

static CPU_STK TaskStk1[TASK_STACK_ZIZE];
static OS_TCB  TaskTCB1;

static CPU_STK TaskStk2[TASK_STACK_ZIZE];
static OS_TCB  TaskTCB2;

static void TaskStart1(void *p_arg)
{
    OS_ERR  err;
    while(1)
    {
        LED_ON();
        OSTimeDly((OS_TICK)10, OS_OPT_TIME_DLY, &err);//延时10个tick
        LED_OFF();
        OSTimeDly((OS_TICK)10, OS_OPT_TIME_DLY, &err);
    }
}

static void TaskStart2(void *p_arg)
{
    OS_ERR  err;
    while(1)
    {
        printf("TaskStart2 looping! \r\n ");
        OSTimeDly((OS_TICK)10, OS_OPT_TIME_DLY, &err);//延时10个tick
    }
}

int main()
{
    OS_ERR err;

    CPU_Init();
    BSP_Init();
    SysTick_Init();

    OSInit(&err);

    OSTaskCreate((OS_TCB    *) &TaskTCB1,              //TCB
                (CPU_CHAR   *) "Task1",                //任务的名字
                (OS_TASK_PTR ) TaskStart1,             //任务处理函数
                (void       *) 0,                      //传参
                (OS_PRIO     ) TASK_PRIO,              //任务优先级
                (CPU_STK    *) &TaskStk1[0],           //任务的栈的底部
                (CPU_STK_SIZE) TASK_STACK_ZIZE / 10,  //栈的watermark
                (CPU_STK_SIZE) TASK_STACK_ZIZE,        //栈的总大小
                (OS_MSG_QTY  ) 0u,                     //这个任务可以接收的消息的最大个数
                (OS_TICK     ) 0u,                     //在round robin中有用,时间片的大小
                (void       *) 0,                      //TCB扩展内存,用于用户自定义的数据
                (OS_OPT      ) (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),//选项
                (OS_ERR     *) &err);


    OSTaskCreate((OS_TCB    *) &TaskTCB2,
                (CPU_CHAR   *) "Task2",
                (OS_TASK_PTR ) TaskStart2,
                (void       *) 0,
                (OS_PRIO     ) TASK_PRIO,
                (CPU_STK    *) &TaskStk2[0],
                (CPU_STK_SIZE) TASK_STACK_ZIZE / 10,
                (CPU_STK_SIZE) TASK_STACK_ZIZE,
                (OS_MSG_QTY  ) 0u,
                (OS_TICK     ) 0u,
                (void       *) 0,
                (OS_OPT      ) (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                (OS_ERR     *) &err);

    OSStart(&err);

    return 0;
}

上面的任务创建之后,会执行任务处理函数TaskStart1TaskStart2TaskStart1不停的亮灭LED,TaskStart2则不停的打印信息。

二. OSTaskCreate函数做了什么?

[os_task.c OSTaskCreate函数 片段1]

void  OSTaskCreate (OS_TCB        *p_tcb,
                    CPU_CHAR      *p_name,
                    OS_TASK_PTR    p_task,
                    void          *p_arg,
                    OS_PRIO        prio,
                    CPU_STK       *p_stk_base,
                    CPU_STK_SIZE   stk_limit,
                    CPU_STK_SIZE   stk_size,
                    OS_MSG_QTY     q_size,
                    OS_TICK        time_quanta,
                    void          *p_ext,
                    OS_OPT         opt,
                    OS_ERR        *p_err)
{
    ...

    CPU_STK       *p_sp;
    CPU_STK       *p_stk_limit;
    CPU_SR_ALLOC();

    ...


    OS_TaskInitTCB(p_tcb);

    *p_err = OS_ERR_NONE;

    /* -------------- CLEAR THE TASK'S STACK -------------- */
    if (((opt & OS_OPT_TASK_STK_CHK) != 0u) ||
        ((opt & OS_OPT_TASK_STK_CLR) != 0u)) {
        if ((opt & OS_OPT_TASK_STK_CLR) != 0u) {
            p_sp = p_stk_base;
            for (i = 0u; i < stk_size; i++) {
                *p_sp = 0u;
                p_sp++;
            }
        }
    }

OS_TaskInitTCB之前是一些入参检查。OS_TaskInitTCB函数是初始化TCB里面的各个成员,基本上都设为0或者DEF_NULL。把TCB的优先级设置为OS_CFG_PRIO_MAX。随后如果opt中的OS_OPT_TASK_STK_CHK比特位和OS_OPT_TASK_STK_CLR比特位设为1,那么就把stack全部清为0。

[os_task.c OSTaskCreate函数 片段2]

#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

根据栈的不同的增长方向来计算p_stk_limitARM对两种增长方式都支持,但是由于编译器的原因一般都是选择CPU_STK_GROWTH_HI_TO_LO,从高地址向低地址增长。

[os_task.c OSTaskCreate函数 片段3]

p_sp = OSTaskStkInit(p_task, p_arg, p_stk_base, p_stk_limit, stk_size, opt);

#if (CPU_CFG_STK_GROWTH == CPU_STK_GROWTH_HI_TO_LO)
    if (p_sp < p_stk_base) {
       *p_err = OS_ERR_STAT_STK_SIZE_INVALID;
        return;
    }
#else
    if (p_sp > p_stk_base + stk_size) {
       *p_err = OS_ERR_STAT_STK_SIZE_INVALID;
        return;
    }
#endif

再来看OSTaskStkInit这个函数:

[os_cpu_c.c 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;

    /* 'opt' is not used, prevent warning                  */
    (void)opt;

    /* Load stack pointer                                  */
    p_stk = &p_stk_base[stk_size];

    /* Align the stack to 8-bytes.                         */
    p_stk = (CPU_STK *)((CPU_STK)(p_stk) & 0xFFFFFFF8u);

    /* Registers stacked as if auto-saved on exception     */
    /* FPU auto-saved registers.                           */
#if (OS_CPU_ARM_FP_EN > 0u)

     --p_stk;

    *(--p_stk) = (CPU_STK)0x02000000u;   /* FPSCR          */

    /* Initialize S0-S15 floating point registers          */
    *(--p_stk) = (CPU_STK)0x41700000u;   /* S15            */
    *(--p_stk) = (CPU_STK)0x41600000u;   /* S14            */
    *(--p_stk) = (CPU_STK)0x41500000u;   /* S13            */
    *(--p_stk) = (CPU_STK)0x41400000u;   /* S12            */
    *(--p_stk) = (CPU_STK)0x41300000u;   /* S11            */
    *(--p_stk) = (CPU_STK)0x41200000u;   /* S10            */
    *(--p_stk) = (CPU_STK)0x41100000u;   /* S9             */
    *(--p_stk) = (CPU_STK)0x41000000u;   /* S8             */
    *(--p_stk) = (CPU_STK)0x40E00000u;   /* S7             */
    *(--p_stk) = (CPU_STK)0x40C00000u;   /* S6             */
    *(--p_stk) = (CPU_STK)0x40A00000u;   /* S5             */
    *(--p_stk) = (CPU_STK)0x40800000u;   /* S4             */
    *(--p_stk) = (CPU_STK)0x40400000u;   /* S3             */
    *(--p_stk) = (CPU_STK)0x40000000u;   /* S2             */
    *(--p_stk) = (CPU_STK)0x3F800000u;   /* S1             */
    *(--p_stk) = (CPU_STK)0x00000000u;   /* S0             */

#endif

    *(--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  */

#if (OS_CPU_ARM_FP_EN > 0u)
    /* R14: EXEC_RETURN; See Note 5     */
    *(--p_stk) = (CPU_STK)0xFFFFFFEDuL;
#else
    /* R14: EXEC_RETURN; See Note 5     */
    *(--p_stk) = (CPU_STK)0xFFFFFFFDuL;
#endif

    /* Remaining registers saved on process stack          */
    *(--p_stk) = (CPU_STK)0x11111111uL;  /* R11            */
    *(--p_stk) = (CPU_STK)0x10101010uL;  /* R10            */
    *(--p_stk) = (CPU_STK)0x09090909uL;  /* R9             */
    *(--p_stk) = (CPU_STK)0x08080808uL;  /* R8             */
    *(--p_stk) = (CPU_STK)0x07070707uL;  /* R7             */
    *(--p_stk) = (CPU_STK)0x06060606uL;  /* R6             */
    *(--p_stk) = (CPU_STK)0x05050505uL;  /* R5             */
    *(--p_stk) = (CPU_STK)0x04040404uL;  /* R4             */

#if (OS_CPU_ARM_FP_EN > 0u)

    /* Initialize S16-S31 floating point registers         */
    *(--p_stk) = (CPU_STK)0x41F80000u;   /* S31            */
    *(--p_stk) = (CPU_STK)0x41F00000u;   /* S30            */
    *(--p_stk) = (CPU_STK)0x41E80000u;   /* S29            */
    *(--p_stk) = (CPU_STK)0x41E00000u;   /* S28            */
    *(--p_stk) = (CPU_STK)0x41D80000u;   /* S27            */
    *(--p_stk) = (CPU_STK)0x41D00000u;   /* S26            */
    *(--p_stk) = (CPU_STK)0x41C80000u;   /* S25            */
    *(--p_stk) = (CPU_STK)0x41C00000u;   /* S24            */
    *(--p_stk) = (CPU_STK)0x41B80000u;   /* S23            */
    *(--p_stk) = (CPU_STK)0x41B00000u;   /* S22            */
    *(--p_stk) = (CPU_STK)0x41A80000u;   /* S21            */
    *(--p_stk) = (CPU_STK)0x41A00000u;   /* S20            */
    *(--p_stk) = (CPU_STK)0x41980000u;   /* S19            */
    *(--p_stk) = (CPU_STK)0x41900000u;   /* S18            */
    *(--p_stk) = (CPU_STK)0x41880000u;   /* S17            */
    *(--p_stk) = (CPU_STK)0x41800000u;   /* S16            */

#endif

    return (p_stk);
}

其中pTask是线程的入口函数,一般来说pTask应该是一个死循环,永远不会退出。
OS_TaskReturn是一个为了防止pTask这个函数最终的退出而导致异常:

[os_task.c OS_TaskReturn函数]

void  OS_TaskReturn (void)
{
    OS_ERR  err;

    /* Call hook to let user decide on what to do    */
    OSTaskReturnHook(OSTCBCurPtr);

#if OS_CFG_TASK_DEL_EN > 0u

    /* Delete task if it accidentally returns!       */
    OSTaskDel((OS_TCB *)0, (OS_ERR *)&err);

#else

    for (;;) {
        OSTimeDly((OS_TICK )OSCfg_TickRate_Hz,
                (OS_OPT  )OS_OPT_TIME_DLY,
                (OS_ERR *)&err);
    }

#endif
}

OSTaskStkInit函数之后,先将栈进行8-bytes对齐,在线程的栈p_stk_base数组中,结构如下(假设开启了FPU):

|===================================================|
| position DWORD |     value     |   Descriptions   |
|===================================================|
|    Top - 0     |  0x02000000u  |       FPSCR      |
|    Top - 1     |  0x41700000u  |        S15       |
|    Top - 2     |  0x41600000u  |        S14       |
|    Top - 3     |  0x41500000u  |        S12       |
|    Top - 4     |  0x41400000u  |        S11       |
|    Top - 5     |  0x41300000u  |        S10       |
|    Top - 6     |  0x41200000u  |        S9        |
|    Top - 7     |  0x41100000u  |        S8        |
|    Top - 8     |  0x41000000u  |        S7        |
|    Top - 9     |  0x40E00000u  |        S6        |
|    Top - 10    |  0x40C00000u  |        S5        |
|    Top - 11    |  0x40A00000u  |        S4        |
|    Top - 12    |  0x40800000u  |        S3        |
|    Top - 13    |  0x40400000u  |        S2        |
|    Top - 14    |  0x3F800000u  |        S1        |
|    Top - 15    |  0x00000000u  |        S0        |
|---------------------------------------------------|
|    Top - 16    |  0x01000000u  |        xPSR      |
|    Top - 17    |     p_task    |   Entry Pointer  |
|    Top - 18    | OS_TaskReturn |      R14 (LR)    |
|    Top - 19    |  0x12121212u  |        R12       |
|    Top - 20    |  0x03030303u  |        R3        |
|    Top - 21    |  0x02020202u  |        R2        |
|    Top - 22    |  p_stk_limit  |        R1        |
|    Top - 23    |     p_arg     |   R0 : argument  |
|    Top - 24    |  0xFFFFFFEDu  | R14: EXEC_RETURN |
|    Top - 25    |  0x11111111u  |        R11       |
|    Top - 26    |  0x10101010u  |        R10       |
|    Top - 27    |  0x09090909u  |        R9        |
|    Top - 28    |  0x08080808u  |        R8        |
|    Top - 29    |  0x07070707u  |        R7        |
|    Top - 30    |  0x06060606u  |        R6        |
|    Top - 31    |  0x05050505u  |        R5        |
|    Top - 32    |  0x04040404u  |        R4        |
|---------------------------------------------------|
|    Top - 34    |  0x41F80000u  |        S31       |
|    Top - 35    |  0x41F00000u  |        S30       |
|    Top - 36    |  0x41E80000u  |        S29       |
|    Top - 37    |  0x41E00000u  |        S28       |
|    Top - 38    |  0x41D80000u  |        S27       |
|    Top - 39    |  0x41D00000u  |        S26  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值