LiteOS-任务篇-源码分析-任务调度函数


前言

  • 20201012
  • LiteOS 2018
  • 建议先瞄一眼 辅助参考代码 章节

笔录草稿

核心源码分析

  • 这里主要分析系统调度的汇编部分,也是调度的底层核心部分。

osTaskSchedule函数源码分析

  • osTaskSchedule 源码 ( 位于文件 los_dispatch_keil.S
    • 往寄存器 OS_NVIC_INT_CTRL 中写入 OS_NVIC_PENDSVSET

      • OS_NVIC_INT_CTRL 为 Interrupt Control State Register,该寄存器可配置内容如下
        • set a pending Non-Maskable Interrupt (NMI)
        • set or clear a pending SVC
        • set or clear a pending SysTick
        • check for pending exceptions
        • check the vector number of the highest priority pended exception
        • check the vector number of the active exception.
      • 设置如图,触发 PendSV 中断
    • 退出 osTaskSchedule 函数,即是返回上层函数

osTaskSchedule
    LDR     R0, =OS_NVIC_INT_CTRL
    LDR     R1, =OS_NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR
  • OS_NVIC_INT_CTRL 定义 ( 位于文件 los_hwi.h
/**
 * @ingroup los_hwi
 * Interrupt control and status register.
 */
#define OS_NVIC_INT_CTRL            0xE000ED04
  • OS_NVIC_INT_CTRL 定义 ( 位于文件 los_dispatch_keil.S
OS_NVIC_PENDSVSET           EQU     0x10000000

osPendSV函数源码分析

  • PendSV 中断的回调函数就是 void osPendSV(void);
  • osPendSV 源码 ( 位于文件 los_dispatch_keil.S
    • 读取 PRIMASK 的值到 R12 中,即是保存中断状态
    • 屏蔽全局中断
    • 判断是否调用 TaskSwitch 函数
      • 如果宏 LOSCFG_BASE_CORE_TSK_MONITORNO,则运行 TaskSwitch 函数
      • 如果宏 LOSCFG_BASE_CORE_TSK_MONITORYES,则在下面运行 osTaskSwitchCheck 函数
        • 压栈保护寄存器 R12LR
        • 运行 R2 函数,也就是 osTaskSwitchCheck 函数
          • 源码解析路径:LOS_KernelInit() --> osTaskMonInit() --> g_pfnTskSwitchHook = osTaskSwitchCheck;
          • 读者可以自己追踪一下
        • 恢复 R12LR
osPendSV
    MRS     R12, PRIMASK
    CPSID   I

    LDR     R2, =g_pfnTskSwitchHook    ; C: R2 = &g_pfnTskSwitchHook;
    LDR     R2, [R2]    ; C: R2 = *R2; ==》 R2 = g_pfnTskSwitchHook;
    CBZ     R2, TaskSwitch    ; C: if(g_pfnTskSwitchHook == 0) TaskSwitch();
    PUSH    {R12, LR}    ; 将 R12 和 LR 寄存器压栈
    BLX     R2    ; 跳到 R2
    POP     {R12, LR}    ; 出栈到寄存器 R12 和 LR
  • PRIMASK 说明
    • 这是个只有单一比特的寄存器
    • 被置 1 后,就关掉所有可屏蔽的异常,只剩下 NMI硬fault 可以响应
    • 缺省值是0,表示没有关中断。
  • 指令 CBZ
    • 比较 为 0 则跳转,如:CBZ x1,fun ; 表示如果 x10,则跳转到 fun
  • 语句 PUSH {R12, LR}POP {R12, LR}个人理解,望指正
    • {} 内先排序,根据寄存器 PS 的走向排序,最终目标是,下面那点
    • 小端模式:低编号寄存器对应低地址
    • PUSH {R12, LR}
      • 顺序:LR R12
      • 压栈:先压 LR,PS -= 4
      • 压栈:再压 R12,PS -= 4
    • PUSH {R12, LR}
      • 顺序:R12 LR
      • 压栈:先出 R12,PS += 4
      • 压栈:再压 LR,PS += 4

TaskSwitch函数源码分析

  • 如果用户没有开启任务堆栈监测,即宏 LOSCFG_BASE_CORE_TSK_MONITOR 配置为 NO,就运行本函数。
  • PSP 更新给 R0
  • 手动把 R4-R12 压栈
    • R0-R3,R12,LR,PC,xPSR 这些寄存器已经自动压栈了
  • 更新当前运行任务栈 g_stLosTask.pstRunTask.pStackPointer 指针
  • 更新当前任务状态,取消 OS_TASK_STATUS_RUNNING 运行态
    • 先获取当前任务的状态寄存器
    • 再取消 OS_TASK_STATUS_RUNNING 运行态
    • 最后再赋值回 g_stLosTask.pstRunTask.usTaskStatus
  • 更新当前运行任务变量 *g_stLosTask.pstRunTask = *g_stLosTask.pstNewTask;
  • 更新准备运行的任务的状态,更新运行态 OS_TASK_STATUS_RUNNING注:此时*g_stLosTask.pstRunTask *g_stLosTask.pstNewTask 是一样的,指向同一个任务
  • 准备运行的任务手动出栈
  • 更新准备运行的任务的 PSP 值
  • 恢复原有的中断状态
  • 返回到上层函数中,如 osScheduleLOS_Schedule 函数中
TaskSwitch
    MRS     R0, PSP    ;// R0 = PSP;

    STMFD   R0!, {R4-R12}    ;// 手动压栈,先减再压,小端,且栈往下生长

    LDR     R5, =g_stLosTask    ;//  R5 = g_stLosTask; ==> R5 = g_stLosTask.pstRunTask;
    LDR     R6, [R5]     ;// R6 = *(g_stLosTask.pstRunTask); ==> R6 = g_stLosTask.pstRunTask.pStackPointer;
    STR     R0, [R6]    ;// *(g_stLosTask.pstRunTask.pStackPointer) = R0;


    LDRH    R7, [R6 , #4]    ;// R7 = *(&(g_stLosTask.pstRunTask.usTaskStatus)); ==> R7 = g_stLosTask.pstRunTask.usTaskStatus;
    MOV     R8,#OS_TASK_STATUS_RUNNING    ;// R8 = OS_TASK_STATUS_RUNNING;
    BIC     R7, R7, R8    ;// R7 &= ~R8;
    STRH    R7, [R6 , #4]    ;// g_stLosTask.pstRunTask.usTaskStatus = R7;


    LDR     R0, =g_stLosTask    ;// R0 = g_stLosTask; ==> R0 = g_stLosTask.pstRunTask;
    LDR     R0, [R0, #4]    ;// R0 = *(g_stLosTask.pstNewTask); ==> R0 = g_stLosTask.pstNewTask.pStackPointer;
    STR     R0, [R5]    ;// g_stLosTask.pstRunTask.pStackPointer = g_stLosTask.pstNewTask.pStackPointer; ==> *g_stLosTask.pstRunTask = *g_stLosTask.pstNewTask;


    LDRH    R7, [R0 , #4]    ;// R7 = *(&(g_stLosTask.pstNewTask.usTaskStatus)); ==> R7 = g_stLosTask.pstNewTask.usTaskStatus;
    MOV     R8,  #OS_TASK_STATUS_RUNNING    ;// R8 = OS_TASK_STATUS_RUNNING;
    ORR     R7, R7, R8    ;// R7 |= R8;
    STRH    R7,  [R0 , #4]    ;// g_stLosTask.pstNewTask.usTaskStatus = R7;

    LDR     R1,   [R0]    ;// R1 = *(g_stLosTask.pstNewTask.pStackPointer);
    LDMFD   R1!, {R4-R12}    ;// 手动出栈,先出栈后增,小端,且栈往上生长
    MSR     PSP,  R1    ;// PSP = R1; // 更新 PSP 值

    MSR     PRIMASK, R12    ;// 恢复原有的中断状态
    BX      LR    ;// 返回到上层函数中,如 `osSchedule` 或 `LOS_Schedule` 函数中

    ALIGN
    END

调度上层源码分析

osSchedule函数源码分析

  • osSchedule 函数多用于创建任务函数和删除任务函数。
/*****************************************************************************
 Function    : osSchedule
 Description : task scheduling
 Input       : None
 Output      : None
 Return      : None
 *****************************************************************************/
LITE_OS_SEC_TEXT VOID osSchedule(VOID)
{
    osTaskSchedule();
}

LOS_Schedule函数源码分析

  • LOS_Schedule 函数为系统常用的调度函数。
  • 简单流程
    • 锁中断
    • 从就绪列表中获取最合适的任务,赋值给 g_stLosTask.pstNewTask ,为下一个运行的任务
    • 判断当前运行的任务和就绪列表中最适合的任务是否为同一个任务
        • 判断是否锁任务调度
            • 解锁中断
            • 进行调度操作: osTaskSchedule();
            • return;
    • 解锁中断
/*****************************************************************************
 Function    : LOS_Schedule
 Description : Function to determine whether task scheduling is required
 Input       : None
 Output      : None
 Return      : None
 *****************************************************************************/
LITE_OS_SEC_TEXT VOID LOS_Schedule(VOID)
{
    UINTPTR uvIntSave;

    uvIntSave = LOS_IntLock(); // 锁中断

    /* Find the highest task */
    g_stLosTask.pstNewTask = LOS_DL_LIST_ENTRY(osPriqueueTop(), LOS_TASK_CB, stPendList); // 从就绪列表中获取最合适的任务,赋值给 g_stLosTask.pstNewTask ,为下一个运行的任务

    /* In case that running is not highest then reschedule */
    if (g_stLosTask.pstRunTask != g_stLosTask.pstNewTask) // 不是同一个任务就进行调度准备
    {
        if ((!g_usLosTaskLock)) // 判断是否锁任务了
        {
            (VOID)LOS_IntRestore(uvIntSave); // 解锁中断

            osTaskSchedule(); // 调度操作

            return; // 返回
        }
    }

    (VOID)LOS_IntRestore(uvIntSave); // 解锁中断
}

辅助参考代码

任务控制块 LOS_TASK_CB 源码参考

  • 上述代码分析理解时需要了解这个结构体布局。
/**
 * @ingroup los_task
 * Define the task control block structure.
 */
typedef struct tagTaskCB
{
    VOID                        *pStackPointer;             /**< Task stack pointer          */
    UINT16                      usTaskStatus;
    UINT16                      usPriority;
    UINT32                      uwStackSize;                /**< Task stack size             */
    UINT32                      uwTopOfStack;               /**< Task stack top              */
    UINT32                      uwTaskID;                   /**< Task ID                     */
    TSK_ENTRY_FUNC              pfnTaskEntry;               /**< Task entrance function      */
    VOID                        *pTaskSem;                  /**< Task-held semaphore         */
    VOID                        *pTaskMux;                  /**< Task-held mutex             */
    UINT32                      uwArg;                      /**< Parameter                   */
    CHAR                        *pcTaskName;                /**< Task name                   */
    LOS_DL_LIST                 stPendList;
    LOS_DL_LIST                 stTimerList;
    UINT32                      uwIdxRollNum;
    EVENT_CB_S                  uwEvent;
    UINT32                      uwEventMask;                /**< Event mask                  */
    UINT32                      uwEventMode;                /**< Event mode                  */
    VOID                        *puwMsg;                    /**< Memory allocated to queues  */
} LOS_TASK_CB;

LiteOS中断向量表(二次命名版)

  • 中断向量表源码 (位于文件 los_hwi.c
HWI_PROC_FUNC m_pstHwiForm[OS_VECTOR_CNT] =
{
    (HWI_PROC_FUNC)0,                    // [0] Top of Stack
    (HWI_PROC_FUNC)Reset_Handler,        // [1] reset
    (HWI_PROC_FUNC)osHwiDefaultHandler,  // [2] NMI Handler
    (HWI_PROC_FUNC)osHwiDefaultHandler,  // [3] Hard Fault Handler
    (HWI_PROC_FUNC)osHwiDefaultHandler,  // [4] MPU Fault Handler
    (HWI_PROC_FUNC)osHwiDefaultHandler,  // [5] Bus Fault Handler
    (HWI_PROC_FUNC)osHwiDefaultHandler,  // [6] Usage Fault Handler
    (HWI_PROC_FUNC)0,                    // [7] Reserved
    (HWI_PROC_FUNC)0,                    // [8] Reserved
    (HWI_PROC_FUNC)0,                    // [9] Reserved
    (HWI_PROC_FUNC)0,                    // [10] Reserved
    (HWI_PROC_FUNC)osHwiDefaultHandler,  // [11] SVCall Handler
    (HWI_PROC_FUNC)osHwiDefaultHandler,  // [12] Debug Monitor Handler
    (HWI_PROC_FUNC)0,                    // [13] Reserved
    (HWI_PROC_FUNC)osPendSV,             // [14] PendSV Handler
    (HWI_PROC_FUNC)osHwiDefaultHandler,  // [15] SysTick Handler
};

参考

链接