您的位置:首页 > 其它

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

2020-10-13 09:32 155 查看

目录
  • 调度上层源码分析
  • 辅助参考代码
  • 参考

    前言

    • 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
    3687
    {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 值
  • 恢复原有的中断状态
  • 返回到上层函数中,如
    osSchedule
    LOS_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
    };

    参考

    链接

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: