前言

  • 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. HDU - 6570 - Wave(暴力)

    Avin is studying series. A series is called "wave" if the following conditions are satisfi ...

  2. C++类重载函数的function和bind使用

    在没有C++11的std::function和std::bind之前,我们使用函数指针的方式是五花八门,结构很繁琐难懂.C++11中提供了std::function和std::bind统一了可调用对象 ...

  3. Oracle的dbms_random.value(min,max)函数包括边界值吗?数据是如何分布的?

    事先申明下,我的DB环境是Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production,不保证在其它版本下也 ...

  4. Mybatis-解决属性名和字段名不一致的问题

    解决属性名和字段名不一致的问题 目录 解决属性名和字段名不一致的问题 1. 问题 2. ResultMap 1. 问题 在数据库中,密码字段为pwd,而在实体类中为password package c ...

  5. javascript事件环微任务和宏任务队列原理

    哈喽!大家好!我是木瓜太香,我又来嘞,今天来说说前端面试中经常别问到的 JS 事件环问题. JS 事件环 JS 程序的运行是离不开事件环机制的,这个机制保证在发生某些事情的时候我们有机会执行一个我们事 ...

  6. 虚拟PWN初探

    前言 之前看到星盟Q群里面的消息,Freedom师傅在B站直播关于虚拟pwn入门的公开课,然后就去听了一波,感觉受益匪浅.之前一直以为虚拟pwn是超级复杂的东西,今年打比赛也遇到了好几次,一直无从下手 ...

  7. 如何在不使用OleDbCommandBuilder情况下使用OleDbDataAdapter更新Access数据库记录

    我在博客园的博问和微软论坛都曾经请教了这个问题(问题链接),可能我的问题太简单,并没有获得太多解答. 到今天为止,我自己通过查找和摸索,基本把这个问题解决了,还是记录下来,供其他朋友参考. 第一次解决 ...

  8. EAM在不同行业的应用

    EAM在不同行业的应用 EAM从出现至今,已让很多资产密集型企业受益,甚至在一些行业领域里已经是公认的.不可或缺的管理方案,比如电力行业.轨道交通行业等.但由于不同行业或者企业业务类型不同,导致了资产 ...

  9. Python爬取天气预报,Ta的城市开始降温了

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取 python免费学习资 ...

  10. @RequestParam,@RequestBody,@ResponseBody,@PathVariable注解的一点小总结

    一.前提知识: http协议规定一次请求对应一次响应,根据不同的请求方式,请求的内容会有所不同: 发送GET请求是没有请求体的,参数会直接拼接保留到url后一并发送: 而POST请求是带有请求体的,带 ...