前言

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

参考

链接

LiteOS-任务篇-源码分析-任务调度函数的更多相关文章

  1. 【LiteOS】LiteOS任务篇-源码分析-创建任务函数

    目录 前言 链接 参考 笔录草稿 部分源码分析 源码分析 LOS_TaskCreate函数 LOS_TaskCreateOnly函数 宏 OS_TCB_FROM_PENDLIST 和 宏 LOS_DL ...

  2. LiteOS-任务篇-源码分析-系统启动函数

    目录 前言 链接 参考 开启调度 LOS_Start 函数源码 osTickStart 函数源码 LOS_StartToRun 函数源码 前言 20201009 移植好内核后,开始实战内核. 源码分析 ...

  3. 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 百篇博客分析OpenHarmony源码 | v4.05

    百篇博客系列篇.本篇为: v04.xx 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度 ...

  4. 性能测试分享: Jmeter的源码分析main函数参数

    性能测试分享: Jmeter的源码分析main函数参数   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大 ...

  5. jQuery源码分析-each函数

    本文部分截取自且行且思 jQuery.each方法用于遍历一个数组或对象,并对当前遍历的元素进行处理,在jQuery使用的频率非常大,下面就这个函数做了详细讲解: 复制代码代码 /*! * jQuer ...

  6. LiteOS-任务篇-源码分析-删除任务函数

    目录 前言 笔录草稿 源码分析 LOS_TaskDelete函数源码分析 完整源码 参考 链接 前言 20201009 LiteOS 2018 需要会通用链表 笔录草稿 源码分析 LOS_TaskDe ...

  7. Memcached源码分析——process_command函数解析

    以下为个人笔记 /** * process_command 在memcached中是用来处理用户发送的命令的, * 包括get set,add,delete,replace,stats,flush_a ...

  8. vue2源码分析:patch函数

    目录 1.patch函数的脉络 2.类vnode的设计 3.createPatch函数中的辅助函数和patch函数 4.源码运行展示(DEMO) 一.patch函数的脉络 首先梳理一下patch函数的 ...

  9. jquery源码分析-工具函数

    jQuery的版本一路狂飙啊,现在都到了2.0.X版本了.有空的时候,看看jquery的源码,学习一下别人的编程思路还是不错的. 下面这里是一些jquery的工具函数代码,大家可以看看,实现思路还是很 ...

随机推荐

  1. Codeforces Round #571 (Div. 2)-D. Vus the Cossack and Numbers

    Vus the Cossack has nn real numbers aiai. It is known that the sum of all numbers is equal to 00. He ...

  2. ctf常见源码泄露

    前言 在ctf中发现很多源码泄露的题,总结一下,对于网站的搭建要注意删除备份文件,和一些工具的使用如git,svn等等的规范使用,避免备份文件出现在公网 SVN源码泄露 原理 SVN(subversi ...

  3. Mybatis源码学习第七天(PageHelper分析)

    其实我本来是不打算写这个PageHelper的,但是后来想了想,还是写了吧!现在市场用Mybatis的产品分页应该差不多都是用PageHelper来实现的,毕竟Mybatis的分页rowbound.. ...

  4. touchstart 事件与 click 事件的冲突

    const clickEvent = (function() {   if ('ontouchstart' in document.documentElement === true)     retu ...

  5. eleLogger主题评论区(反馈与建议)

    // run $('#blog_post_info, #BlogPostCategory, #post_next_prev, #blog_post_info_block').remove() $('# ...

  6. A Funny Game(POJ 2484)

    原题如下: A Funny Game Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7108   Accepted: 446 ...

  7. pytest测试框架 -- setup和teardown等

    一.用例运行级别 1.函数级别(setup.teardown 或 setup_function.teardown_function): 仅对处于同作用域的测试函数有效(该函数定义不在类中,则对非类中测 ...

  8. [LeetCode] 46. 全排列(回溯)

    ###题目 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], ...

  9. CSS、bootstrap4等相关疑难杂症

    说明 本篇博客仅用于个人随笔,所以内容比较随意. 在bootstrap4中,引入样式后,按钮.输入框等组件的选取状态会出现黑色加重边框,该如何解决? 解决示例: input:focus{outline ...

  10. Infor EAM:注重行业属性,实现对轨道交通线性资产的可视化管理

    Infor EAM:注重行业属性,实现对轨道交通线性资产的可视化管理 企业得利,一要开源,二要节流.而企业资产管理的目的,也正是从资产的角度出发,一方面通过相关资源与活动的合理安排提高设备可利用率.增 ...