当前位置: 首页 > news >正文

煤炭网站建设企业网站名称价格

煤炭网站建设企业,网站名称价格,金山网站建设费用,广州网站建设网站优化网站推广#x1f431;作者#xff1a;一只大喵咪1201 #x1f431;专栏#xff1a;《RTOS学习》 #x1f525;格言#xff1a;你只管努力#xff0c;剩下的交给时间#xff01; 目录 #x1f3c0;认识任务切换#x1f3d0;切换的实质#x1f3d0;栈中的内容#x1f3d0;切… 作者一只大喵咪1201 专栏《RTOS学习》 格言你只管努力剩下的交给时间 目录 认识任务切换切换的实质栈中的内容切换过程 实现任务切换伪造现场启动任务切换任务 栈和寄存器变化创建任务时任务启动时任务切换时 总结 认识任务切换 切换的实质 如上图所示代码定义两个任务函数task_a和task_b在mymian函数中调用这两个函数在调用的时候传入不同的参数。在任务函数中打印出自己的函数名称后便开始死循环打印各自形参接收到的字符串。 如上图所示在调用task_a以后该函数在它的栈中运行局部变量保存在栈中在其内部调用的puts和putchar函数也会有自己的栈这两个函数的栈紧挨着task_a的栈。 假设能执行到task_b函数该函数也有一个栈进行和上面相同的操作。 每一个函数都对应着一个自己的栈。 而FreeRTOS执行的任务也是函数它们也有自己的栈每一个任务就对应着一个自己的栈。 裸机程序中函数在执行的过程中使用的是函数自己的栈中的内容各自的操作也是在自己的栈中完成包括数据的保存修改等等。 FreeRTOS中不同任务在执行的时候也是使用任务函数自己栈中的内容各自的操作也是在自己的栈中完成的。 任务切换的本质就是切换不同任务的栈让CPU来操作。任务切换其实就是在切换栈。 栈中的内容 我们知道FreeRTOS中任务的切换是在SysTick_Handler中断中完成的也就是说在该中断函数中完成了栈的切换。 首先要知道的就是在切换栈的时候栈中有什么 如上图所示便是在SysTick_Handler中切换任务时当前任务栈中的内容包含R0~R3,R12,LR,返回地址,xPSR以及R4~R11这些寄存器中的内容。 在学习中断的保存现场时候本喵讲解过 在产生中断调用中断服务函数之前硬件保存R0~R3,R12,LR,返回地址,xPSR这些寄存器中的数据到栈中。 在中断服务函数中软件保存R4~R11这些寄存器中的数据到栈中。 硬件保存不用我们管软件保存就需要代码来实现了但是我们在写中断服务函数的时候从来也没有写过保存R4~R11寄存器的代码这是因为这部分代码由编译器替我们完成了。 如果中断函数或者普通函数会使用到R4~R11寄存器那么在一进入函数时需要先保存这些寄存器中的值到栈中调用完毕时再将原本的值恢复到寄存器中。 切换过程 如上图所示任务A执行一定时间后产生了Tick中断在中断函数中切换成了任务B随后开始执行任务B执行一定时间后再次在Tick中断函数中切换成任务A如此反复。 关键就在于SysTick_Handler中断服务函数中到底做了什么 暂停任务A保存任务A的现场 R0~R3,R12,LR,返回地址,xPSR由硬件保存到任务A的栈中R4~R11由软件保存到任务A的栈中 运行任务B恢复任务B的现场 任务B栈中的R4~R11寄存器值由软件恢复到寄存器中任务B栈中的R0~R3,R12,LR,返回地址,xPSR寄存器值由硬件恢复到寄存器中。 实现任务切换 伪造现场 继续看这张图任务A和B的切换发生在Tick中断函数中在切换任务执行A的时候需要恢复任务A的现场但是在任务A第一次运行的时候它的现场哪里来的呢 在第一次执行任务A时它的现场并不存在因为没有在执行任务A期间发生过Tick中断所以任务A的栈中没有硬件保存的R0~R3,R12,LR,返回地址,xPSR寄存器值也没有软件保存过R4~R11寄存器的值。 所以需要在创建任务的时候伪造一个该任务的现场在第一次执行该任务的时候有现场可恢复 如上图所示代码定义创建任务函数create_task该函数的第一个参数就是要执行的任务函数所以使用typedef void(*task_function)(void* param)重名名任务函数指针为task_function方便后面使用。 在创建任务的函数create_task中伪造该任务现场时需要的参数有 要执行的任务函数指针f传给任务函数的参数prama属于该任务的栈stack以及栈的大小stack_len 这里本喵仅实现静态任务创建由我们自己指定任务的栈。 在函数内部由于形参stack指向的是栈的起始地址也就是这段内存的最低地址但是栈是从高地址向低地址向下生长的所以需要得到栈顶的位置top因为存放的16个32位的寄存器值所以(stack stack_len)得到的就是栈顶的地址。 下一步就是真正的伪造现场用值填充这个栈由于高地址存放的是高编号寄存器中的值这个规则所以我们从栈底开始依次存放数据。 先存放R4~R11再存放R0~R3,R12,LR,返回地址,xPSR 存放过程中必须严格按照前面讲解的SysTick_Handler中断函数中栈中的内容顺序存放。 对于R4~R11任务第一次启动时并不关心它里面的内容是什么所以全部设置为0R0是用来传递任务函数的参数param的不能设置为0R1~R3,R12也无所谓全部设置为0。 对于LR由于该任务是第一次运行它必然不会是被其他函数调用的所以无所谓返回地址也设置为0。 对于返回地址这才是Tick中断函数第一次启动任务后退出Tick中断函数时真正的返回地址。将任务函数的地址放在这里任务启动并退出中断函数后就会执行相应的任务函数所以这里放入任务函数的指针f。 对于状态寄存器xPSR虽然任务是第一次执行之前没有任何状态存在但是它不能是0必须将它的第24位置1 如上图所示程序状态寄存器xPSR中的第24位T该位为1表示使用Thumb指令集为0表示使用ARM指令集而本喵使用的Cortex-M3只能使用Thumb指令集所以该位必须是1。 现场伪造完毕以后需要将该任务的栈记录下来方便下次切换时能够找到该任务的栈 创建全局数组task_stacks存放每个任务的栈顶使用task_count计数任务个数 如上图所示创建两个大小为1024字节的char类型静态全局数组stack_a和stack_b这两个数组就是两个任务的栈。 在常规创建数组语句的后面加上__attribute__ ((aligned (4)))表示让该数组在内存中要4字节对齐。 如果不要求4字节对齐的话在将该数组作为任务的栈时由于CPU是32位处理器所以在访问栈时可能由于不是4字节对齐而出现读取或者写入错误导致程序出现问题。 如此在创建任务的时候就完成了现场伪造。 启动任务 如上图代码所示在创建好两个任务之后任务的启动也是在Tick中断函数中该中断函数本喵定义成一个汇编函数SysTick_Handler_asm。 在Tick中断产生后硬件会调用SysTick_Handler_asm中断服务函数在一进入中断函数时硬件已经完成了R0~R3,R12,LR,返回地址,xPSR寄存器的保存将这些寄存器中的值保存到了栈中。 但是R4~R11寄存器中的值硬件并不管需要软件完成所以在一进入中断函数时就使用STMDB SP!, {R4 - R11}汇编指令将R4~R11的值保存到栈中。 由于是中断函数所以此时LR寄存器中的值并不是返回地址而是那个特殊值由于在后面会使用BL SysTick_Handler调用C函数来实现任务栈的切换会改变LR中的值所以这里要将此时LR中的特殊值也保存到栈中。 将LR中的特殊值赋给R0进行传参将真正栈(不包括栈中的LR)赋值给R1作为另一个参数进行传参。 在任务没启动时现场保护所操作的栈并不是我们指定的任务栈而默认的栈。 如上图代码所示这是Tick中断中调用的回调C函数SysTick_Handler在函数内部先调用is_task_running()判断任务有没有创建好如果没有创建好就直接返回此时这仅仅是一次普通的Tick中断。 如果任务创建好了根据cur_task当前任务的值判断这是不是第一次启动任务如果该值是-1说明这是第一次启动任务调用get_stack从记录任务栈的数组中得到第一个任务的栈然后调用StartTask_asm函数将该任务的栈切换过来开始执行。 调用StartTask_asm传的参数中stack是指定要切换的栈lr_bak是特殊值是为了软件恢复完毕后触发硬件恢复。 如上图所示代码StartTask_asm函数本喵同样定义成了一个汇编代码在一进入该函数时就将存放在要启动任务栈中的R4~R11值恢复到对应的寄存器中。 R0寄存器中的值是在调用StartTask_asm时传过来的表示要启动任务的栈。 软件恢复完毕后此时的R0指向栈中存放硬件要恢复的R0值所在的位置所以使用MSR MSP, R0将该栈指针赋值给MSP让真正的栈顶指针SP来管理这部分栈。 然后使用BX R1跳转由于R1中的是一个特殊值所以触发了硬件恢复将栈中剩下的R0~R3,R12,LR,返回地址,xPSR值恢复到了对应的寄存器中。 由于此时恢复的现场是我们伪造出来的所以返回地址是该任务要执行的任务函数。 此时第一个任务就执行起来了。 补充用到的功能函数 如上图所示是用到的功能函数这些函数都放在task.c文件中。启动任务只是将全局变量标志task_running置一然后陷入死循环此时在发生Tick中断时通过该标志就可以知道任务是否创建成功因为任务没有创建的时候也有可能发生Tick中断。 切换任务 如上图所示在切换任务时仍然是在Tick中进行的在调用SysTick_Handler回调C函数时已经完成了现场保护在C函数中执行的是红色框中的代码。 任务切换时说明在前面已经有任务在运行了得到前面的任务再调用get_next_task()得到要切换的新任务然后判断这是否只有一个任务如果新任务和前一个任务相同的话就说明只有一个任务此时直接返回就可以现场保存和现场恢复等操作都发生在这个任务上。 不止一个任务时使用set_task_stack()将前一个任务的栈继续保存到记录任务栈的数组中因为前一个任务在运行过程中栈会变化然后获取新任务的栈再更新当前任务的下标cur_task最后调用StartTask_asm来切换任务。 StartTask_asm中的操作和启动第一个任务时一样也是进行软件恢复和硬件恢复然后返回到新任务函数处执行此时就完成了任务的切换。 如此一来两个任务就可以交替执行了。 将SysTick定时器的超时时间设置成1ms此时每隔1ms就会发生一次任务切换两个任务交替执行。 在Tick中断发生时硬件会保存当前任务的R0~R3,R12,LR,返回地址,xPSR寄存器的值到当前任务的栈中其中返回地址就是发生中断时那条指令的下一条地址。 当任务再次被切换回来以后会将返回地址赋值给PC该任务就会接着被切换走的位置继续执行。 只有第一次启动任务和第一次被切换上来的任务返回地址是任务函数的地址任务从函数的起始位置执行。 如上图所示在创建两个任务a和b时传给任务函数的参数分别是a和b运行起来后可以看到串口中字符a和b在交替打印。 如上图所示再创建一个task_c任务来进行一些计算并且打印计算结果。 如上图所示此时串口中打印出来的结果有字符a和b还有计算任务计算的结果三个任务在同时运行也完美的实现了任务之间的切换。 栈和寄存器变化 现在本喵已经自己实现了多任务之间的切换下面来看看每个过程中栈和寄存器的变化。 创建任务时 创建任务的时候在create_task中伪造了现场 如上图所示严格按照寄存器在栈中的存放顺序伪造好了现场这个栈是我们在创建任务时给该任务指定的栈也就是那个全局数组。 下面来看看创建任务时真正内存中的值和我们分析的是否一致 如上图所示打开该工程的反汇编文件找到stack_a全局数组所在的地址是0x2000000c本喵这里仅带大家看一个任务a的创建。 如上图所示首先要根据stack_a全局数组的地址计算出任务A栈的起始地址0x2000000C 1024 0x2000040C又因为伪造现场时栈中存放16个寄存器的值所以0x2000040C 16 * 4 0x200003CC得到就是伪造完现场后任务A栈的SP所在位置。 将SP所指位置在调试过程中查看该位置的内存可以清楚的看到从0x200003CC处开始向高地址增长的内存中存放着我们要伪造的16个寄存器数据。 伪造的R0处存放的值是0x00000061这是任务A函数的形参也就是字符a的ASCII码。伪造的返回地址处存放的值是0x0800054D这是任务A要执行函数的入口地址。 如上图所示从反汇编文件中查找0x0800054C发现这是task任务函数的入口地址。之所以在内存中存放的是0x0800054C 1 0x0800054D是因为最低位为1表示该函数使用的是Thumb指令集。 任务启动时 如上图所示在SysTick_Handler_asm中断函数中打断点让程序在启动任务时第一次进入Tick中断中完成软件保存后停下来。 从此时寄存器中的值可以看到SP 0x2000FFBC此时操作的栈就是这里红色框中内存里的值是软件将对应寄存器中的值保存到栈中蓝色框中是在产生Tick中断时硬件将对应寄存器中的值保存到栈中。此时完成了现场保护。 此时的栈0x2000FFBC和我们指定的任务A的栈0x2000000C相差甚远所以必然不是一个栈。 所以第一次启动任务过程中Tick中断产生后保存现场发生在程序启动时默认的栈中不属于任何一个任务的栈。 如上图在Tcik中断函数中完成现场保护以后会调用回调函数SysTick_Handler在调回调函数中获取到了任务A的栈stack 0x200003CC和我们前面计算出来的结果相符。 如上图所示回调函数的又调用StartTask_asm汇编函数来完成现场恢复在调用汇编函数时传入的参数stack 0x200003CC lr_bak 特殊值。 在汇编函数中首先把任务A栈中伪造的R4~R11的值通过软件恢复到对应的寄存器中此时R0寄存器指向任务A栈里伪造的R0处硬件恢复就从这里开始。 然后使用MSR MSP, R0将任务A的栈交给了SP寄存器管理。 此时我们伪造的任务A栈里的现场仅剩下R0~R3,R12,LR,返回地址,xPSR的值等待硬件恢复了。 如上图所示在StartTask_asm函数中完成软件恢复以后用指令BX R1触发硬件恢复此时R1中的值是由回调函数传递过来的特殊值。 硬件恢复完成以后可以看到程序从我们伪造的返回地址0x0800054D处开始执行也就是任务A函数task而且此时寄存器SP 0x20000040C该值是前面我们算出来的任务A栈的起始顶部位置。 此时任务A在执行过程中操作的就是属于该任务的栈了完成了栈的切换。 任务切换时 如上图所示在任务A启动以后执行的过程中再次产生了Tick中断打断点让程序停止在中断函数中。 在断点位置已经完成了现场保存红色框中的部分是软件将LR,R4~R11寄存器中的值保存到栈中蓝色框中的部分是硬件将R0~R3,R12,LR,返回地址,xPSR保存到栈中。 SP 0x200003C4该值位于任务A的栈0x2000000C~0x2000040C之间。 所以说现场保存发生在任务A的栈中此时就保存了任务A的现场。 如上图所示中断函数再次调用了回调C函数SysTick_Handler在该函数中得到了任务B的栈stack 0x200007CC从内存窗口中可以看到任务B栈里存放的是创建任务B时伪造的现场。 因为任务B是第一次被执行所以恢复的是创建任务时伪造出来的现场。 可以看到任务B栈中的返回地址也是0x0800054D这是因为任务A和任务B执行的是一个任务函数。 如上图回调函数再次调用了StartTask_asm函数来完成任务B的现场恢复在该函数中软件先恢复任务B栈中R0~R11的值到对应寄存器中然后将此时的R0 0x200007EC赋值给SP等待硬件从这里开始恢复剩下的R0~R3,R12,LR,返回地址,xPSR。 如上图在StartTask_asm中使用BX R1触发硬件恢复以后蓝色框中的R0~R3,R12,LR,返回地址,xPSR由硬件恢复到了对应的寄存器中而且此时SP 0x2000080C这是任务B栈的栈顶位置。 此后任务B在执行过程中使用的就是它自己的栈。 此时就恢复了任务B的现场当再次产生Tick中断时就会保存任务B的现场恢复任务A的现场如此往复就实现了两个任务之间的切换。 总结 实时操作系统中最重要的就是多任务之间的切换在这里我们自己动手实现了一遍对任务的切换有了一个更深的认识。 要时刻记住任务切换的本质就是栈的切换任务创建的本质就是伪造现场。
http://www.w-s-a.com/news/427874/

相关文章:

  • 东莞高端品牌网站建设软件开发模型及特点
  • 个人网站的设计与实现的主要内容网站开发公司架构
  • 浏览器收录网站什么是新媒体营销
  • 上海营销网站建设公司下面哪个不是网页制作工具
  • 有哪些网站可以做设计比赛苏州设计公司排名前十
  • 公益网站建设需求车陂手机网站开发
  • 高端网站建设专业营销团队宁德网站建设51yunsou
  • 网站如何做cdn购物网站建设app开发
  • 简单的手机网站模板好看大方的企业网站源码.net
  • 沈阳住房和城乡建设厅网站网站个人备案做论坛
  • 企业建网站的目的开家网站建设培训班
  • 做怎么网站网站优化和推广
  • 建站工具 风铃网站每年空间域名费用及维护费
  • 网站开发工具 知乎工业软件开发技术就业前景
  • 永济微网站建设费用新手如何自学编程
  • 在本地怎么做网站深圳保障房申请条件2022
  • 广州天河区网站建设公司东莞网络游戏制作开发
  • 哪个网站做免费小程序rio门户网站的制作
  • 短网站生成查询网站所有关键词排名
  • 阿里云购买网站登录技术服务外包公司
  • 淘宝单页面网站手机制作游戏的软件
  • 汉中市网站建设wordpress编辑器好麻烦
  • 织梦做的网站快照被攻击在线看crm系统
  • 青岛物流公司网站建设网站建设提议
  • 企业网站建设高端品牌宿州注册公司多少钱
  • 个人微信公众号怎么做微网站吗湛江网站制作方案
  • 学校网站改版南京展厅设计装修
  • 手机网站有免费做的吗建设银行网站不能登录
  • 树莓派做影视网站网站建设企业 熊账号
  • 网站iis7.5配置免费网站建设模板下载