先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题,二维码如下:

一 概述

在上一篇文章《ucos-ii嵌入式操作系统任务调度(一)----任务调度的过程及实现原理》中,我们分析了任务调度过程中的步骤,但是在最后结尾处,我们留了一个小疑问:任务是如何实现切换的呢?在切换的瞬间cpu发生了什么?汇编函数OSCtxSw实现原理又是什么?
在分析这个疑问之前,先看如下几个问题:

  1. CPU的栈是什么?
  2. CPU在执行代码,调用函数的过程中,如何传递函数参数?如何正确返回被调用函数的?
  3. CPU发生中断/异常时,如何响应中断/异常,响应完之后,又是如何跳转回发生异常/中断的地方,继续执行代码的
    先看第一个问题
    栈:首先从物理意义上来看,它就是位于芯片内部的一端RAM内存(**当然这个说法不一定准确,我理解的是外部sdram是不是也可以作为栈区域,不一定是芯片内部的RAM内存,在我的印象中,会在linker file指定栈地址,欢迎各位大佬不吝赐教 **)。其次从软件代码操作的角度来看,它是仅在表头进行插入和删除操作的线性表,也就说对于码农来说,我们只能在一端读取/写入栈。根据芯片架构的不同,又分为两种栈,栈空间地址向上增长和栈空间地址向下增长。
    这个栈是sp指针实时指向的栈。
    再看第二个问题
    如何实现函数调用时,参数传递?
    针对ARM平台,其指令集规定,当参数不超过4个时,我们使用cpu内部的R0~R3来传递参数,如果函数参数超过4个,比如说对于printf函数,我们使用栈来传递参数。
    再看第三个问题
    cpu发生中断、异常时,会先进行现场保护,把当前cpu各个寄存器的值保存在栈中(R0~R15寄存器,当然就包括了sp指针,pc指针),保存完之后,就会调转到中断,异常处执行,执行完成之后,就会从栈中恢复当前的状态,并继续运行。
    上面三个问题,在这里也是粗浅的做了一些说明,在此就不展开来详细说明,读者可以自行查阅资料

二 过程分析

有了上面的基础认识之后,我们现在来看一下,ucos任务切换的瞬间,发生了什么。
首先任务切换,是通过调用宏OS_TASK_SW(),即汇编函数OSCtxSw实现的,OSCtxSw函数如下:

OSCtxSwLDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)LDR     R1, =NVIC_PENDSVSETSTR     R1, [R0]BX      LR

该汇编函数的目的,就是将寄存器NVIC_INT_CTRL设置为NVIC_PENDSVSET,查阅cortex-m系列内核手册可知,就是使能了PendSV异常,也就是说在这里软件触发了一次异常。
既然这个时候,软件触发了一次异常,根据我们上面的知识,可以知道,在响应异常处理时,cpu会自动先进行现场保存,也就是会将当前cpu各个寄存器的值写入到栈中(这个栈是cpu指针真正指向的栈,也就是我们在linker file中指定的内存区域),保存完之后,就会响应异常处理了,异常处理函数如下:

PendSV_HandlerCPSID   I                                                   ; Prevent interruption during context switchMRS     R0, PSP                                             ; PSP is process stack pointerCBZ     R0, OS_CPU_PendSVHandler_nosave                     ; Skip register save the first timeSUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stackSTM     R0, {R4-R11}LDR     R1, =OSTCBCur                                       ; OSTCBCur->OSTCBStkPtr = SP;LDR     R1, [R1]STR     R0, [R1]                                            ; R0 is SP of process being switched out

逐句分析

CPSID   I          ;关闭中断,防止切换期间被打扰,在切换期间有可能产生新的中断,更新堆栈中的信息
MRS     R0, PSP    ;将cpu的堆栈指针psp送给R0,此时R0也指向栈顶
CBZ     R0, OS_CPU_PendSVHandler_nosave ;如果 R0 = 0,跳到OS_CPU_PendSVHandler_nosave,即不保存上下文;如果R0 != 0,则继续往下执行;为什呢?因为首次调用时,是没有上文的,我们只需要切换到下文即可
;保存上文 (如果是第一次切换,则不执行下面的语句,直接执行OS_CPU_PendSVHandler_nosave)
SUBS    R0, R0, #0x20   ;因为寄存器是32位的,4字节对齐,自动压栈的寄存器有8个,所以偏移为8*0x04=0x20
STM     R0, {R4-R11}   ;除去自动压栈的寄存器外,需手动将R4-R11压栈
 LDR     R1, =OSTCBCurLDR     R1, [R1] STR     R0, [R1] ;这里为什么是三行?ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令,先让R1=OSTCBCur(注意这里R1存放的是一个地址),然后将这个地址指向的值赋值给R1,最后再将R1中的值付给R0,其实最终目的就是将R0寄存器值,写入到当前用户分配的堆栈中去

接着就是切换到下文了,如下:

OS_CPU_PendSVHandler_nosavePUSH    {R14}                                               ; Save LR exc_return valueLDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook();BLX     R0POP     {R14}LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;LDR     R1, =OSPrioHighRdyLDRB    R2, [R1]STRB    R2, [R0]LDR     R0, =OSTCBCur                                       ; OSTCBCur  = OSTCBHighRdy;LDR     R1, =OSTCBHighRdyLDR     R2, [R1]STR     R2, [R0]LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stackADDS    R0, R0, #0x20MSR     PSP, R0                                             ; Load PSP with new process SPORR     LR, LR, #0xF4                                       ; Ensure exception return uses process stackCPSIE   IBX      LR                                                  ; Exception return will restore remaining context

同样,逐句分析

PUSH    {R14}                          ; LR压栈,下面要调用C函数,调用C函数时,将用来保存返回的函数地址
LDR     R0, =OSTaskSwHook              ; 调用OSTaskSwHook();
BLX     R0                             ; 调用OSTaskSwHook()结束并返回
POP     {R14}                          ; 恢复 LR 寄存器,与前面对应
LDR     R0, =OSPrioCur                 ; 置OSPrioCur = OSPrioHighRdy;
LDR     R1, =OSPrioHighRdy
LDRB    R2, [R1]
STRB    R2, [R0]
LDR     R0, =OSTCBCur                  ; 置OSTCBCur  = OSTCBHighRdy;
LDR     R1, =OSTCBHighRdy
LDR     R2, [R1]
STR     R2, [R0]
LDR     R0, [R2]                       ; R0中的值为新任务的SP; SP = OSTCBHighRdy->OSTCBStkPtr;
LDM     R0, {R4-R11}                   ; 堆栈中的值手动弹出到寄存器:R4-R11
ADDS    R0, R0, #0x20
 MSR     PSP, R0                        ; PSP = 新任务SP,将SP的指针指向堆栈的顶端
ORR     LR, LR, #0x04                  ; 确保异常返回后使用PSP
CPSIE   I                              ; 重新打开中断,与开始关闭中断对应
BX      LR                             ; 退出异常,从PSP自动弹出xPSR,PC,LR,R0-R3,进入新任务运行

ucos-ii嵌入式操作系统任务调度(二)----任务切换瞬间cpu做了什么以及任务任务切换函数OS_TASK_SW相关推荐

  1. 基于STM32的简易示波器的UCOS II嵌入式实时操作系统实现

    基于STM32的简易示波器的UCOS II嵌入式实时操作系统实现 在基于STM32的示波器的实现的基础上,在STM32上移植UCOS II嵌入式实时操作系统. 在UCOS II操作系统中将各个功能分发 ...

  2. 优先级调度算法实现_一篇讲透嵌入式操作系统任务调度

    进互联网公司操作系统和网络库是基础技能,面试过不去的看,这里基于嵌入式操作系统分几章来总结一下任务调度.内存分配和网络协议栈的基础原理和代码实现. 处理器上电时会产生一个复位中断,接下来会执行复位中断 ...

  3. 从0到1写嵌入式操作系统---------------------------4尝试两个任务的切换

    上次我们创建了两个任务task1与task2,这次我们来实现tsak1与task2两个任务的切换.先了解下CM3内核的一些常用指令,不一定非要记住,只需要熟能生巧!先来看MSR和MRS指令. MRS  ...

  4. RTOS ---嵌入式操作系统之时钟节拍下的任务切换

    嵌入式操作系统之时钟节拍下的任务切换 嵌入式操作系统如FreeRTOS.FreeRTOS 中任务切换的过程, 提到触发任务切换的两种情况 : 高优先级任务就绪抢占和同优先级任务时间共享(包括提前挂起) ...

  5. 基于嵌入式操作系统VxWorks的多任务并发程序设计(3)――任务调度

    基于嵌入式操作系统VxWorks的多任务并发程序设计(3) ――任务调度 作者:宋宝华  e-mail:[email]21cnbao@21cn.com[/email] 出处:软件报 VxWorks支持 ...

  6. 51单片机中使用ucos ii的优缺点(好文)

    摘要:近年来,在单片机系统中嵌入操作系统已经成为人们越来越关心的一个话题.本文通过对一种源码公开的嵌入式实时操作系统ucos ii的分析,以51系列单片机为例,阐述了在单片机中使用该嵌入式操作系统的优 ...

  7. 嵌入式操作系统µCOS-II及应用编程

    点击打开链接 嵌入式操作系统µCOS-II及应用编程 µCOS-II是一个嵌入式实时操作系统,广泛的应用于控制系统中.µCOS-II有Micrium公司提供,是一个可移植.可固化的.可裁剪的.抢占式多 ...

  8. 嵌入式操作系统复习——详细

    嵌入式操作系统 绪论 嵌入式系统 嵌入式操作系统 嵌入式系统硬件和软件基础 嵌入式硬件系统基本组成 嵌入式微处理器 嵌入式系统总线 嵌入式系统存储结构 嵌入式软件体系结构 嵌入式操作系统体系结构 操作 ...

  9. 自制嵌入式操作系统 DAY1

    遥想当年刚学习操作系统的时候,很难理解教科书中关于线程/进程的描述.原因还是在于操作系统书上的内容太过抽象,对于一个没有看过内核代码的初学者来说,很难理解各种数据结构的调度.后来自己也买了一些造轮子的 ...

最新文章

  1. 论机器学习领域的内卷
  2. 如何改变eclipse控制台编码
  3. 重构-改善既有代码的设计:重构原则(二)
  4. leetcode-832-Flipping an Image
  5. BrnShop开源网上商城第二讲:ASP.NET MVC框架
  6. mysql 1033 frm_MySQL ERROR 1033 (HY000): Incorrect information in file. 处理一例
  7. linux c自写时钟,关于internal_add_timer函数(linux/kernel/timer.c中定义的)的一个问题
  8. 看地形地貌下载什么地图?看地形地貌软件介绍
  9. 关于Git这一篇就够了
  10. asp.net知识共享平台
  11. java 实现搜索附近人功能
  12. 计算机网络未识别网络,电脑网络出现未识别的网络,无Internet访问的解决办法...
  13. 图片太大导致 imageView无法显示
  14. stc89c52rc转移到面包板,使用oled屏
  15. [USACO18JAN] Lifeguards S
  16. 关于如何开启本地代理隐藏本地ip
  17. linux服务器端 postfix+php邮件发送+发件人代发修改配置
  18. redistemplete请求spring security /oauth/token 报401错误,表示没有权限
  19. 光线投射与光线跟踪算法归纳
  20. 解决Could not determine artifacts for XXXX: Skipped due to earlier error

热门文章

  1. 智启联云GPS定位平台二次开发接口,支持808/1078设备接入
  2. 中国工业经济-污染与经济增长面板数据熵值法计算教程
  3. Chilledwindouws电脑病毒
  4. 写一个自由曲面光学透镜设计文章的提纲,要比较具体
  5. 从蒙娜丽莎到战争机器 — 达芬奇大师科学展
  6. mybatis基本操作流程
  7. 西门子atch指令详解_西门子PLC指令表-技术中心-智慧矿山-煤矿自动化,煤矿自动化系统,煤矿综合自动化,科达自控—煤矿生产无人值守的推动者,践行者和领导者...
  8. MATLAB绘图有锯齿
  9. 简历中尽量不要出现精通_“熟练”“精通”,这些词在简历中要慎用 | 求职干货记...
  10. java 四则运算_java四则运算