zoukankan      html  css  js  c++  java
  • 【LiteOS】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
    };
    

    参考

    链接

  • 相关阅读:
    ASP.NET应用程序开发
    SQL语句导入导出大全
    批量insert数据
    fiddler抓包时,出现的 Tunnel to ***** : 443
    fidder如何模拟设置断点
    fidder :filter过滤,捕捉指定会话
    vs2005生成注释的快捷键
    保护我们的眼睛,让网页中的文字更易读
    推荐一个FireFox 插件SQLite Manager
    SQLServer2005 xp_cmdshell存储的使用
  • 原文地址:https://www.cnblogs.com/lizhuming/p/13806949.html
Copyright © 2011-2022 走看看