韦东山老师 RTOS 入门课程(二)理解任务的创建,切换过程

RTOS 的核心实现:保存,恢复现场

接下来开始尝试实现 RTOS。当然我们开发的时候其实不用这样做,现在尝试实现只是为了更好地理解原理。

RTOS 的核心就是刚才在研究的问题:保存和恢复现场。再追其本质,其实就是所有寄存器存入栈中了,以此保存。

从上面的代码中可以发现,临时变量都是在每个过程自己的栈中的,而每一个过程都有自己的栈,这个相当于自动保存了。只要不去破坏,就不会收到影响(比如数组越界,可能就会影响到其他函数的栈)。

1692447577446

那么我们首先要实现一个创建任务的函数。创建任务要求如下:

  1. 任务保存现场,每个任务有自己独立的一套栈;
  2. 执行任务;
  3. 恢复现场。

创建任务

我们写一个创建任务函数,传入一个函数指针(这个函数是待执行的任务),传入这个函数执行需要的参数,传入一个栈数组来存取栈。这是一种伪造现场,就是实际上栈的实现是我们自己写程序写了一个数组来存,而不是硬件实现的。

栈我们通过一个 1024 个字节的数组来伪造一个栈,可以存放 256 个寄存器数据(int 也是32位的,所以这里我们用 int 指针来表示栈指针)。

数组自己是从低地址往高地址增长的,栈指针则相反,所以拿到数组基址后要先+1024 跳转到

void create_task(task_function f, void *param, char *stack, int stack_len)
{int *top = stack + stack_len;//sp 指针,位于 stack[1024] 处,stack 是 char 类型数组/* 伪造现场 */top -= 16;//要存16个数据,如下,就是r0-r12,lr,返回地址,xPSR/* r4~r11 */top[0] = 0; /* r4 */top[1] = 0; /* r5 */top[2] = 0; /* r6 */top[3] = 0; /* r7 */top[4] = 0; /* r8 */top[5] = 0; /* r9 */top[6] = 0; /* r10 */top[7] = 0; /* r11 *//* r0~r3 */top[8]  = param; /* r0 */top[9]  = 0; /* r1 */top[10] = 0; /* r2 */top[11] = 0; /* r3 *//* r12,lr */top[12] = 0; /* r12 */top[13] = 0; /* lr *//* 返回地址 */top[14] = f; /* 任务入口 *//* PSR */top[15] = (1<<24); /* psr 使用thumb指令集 */	/* 记录栈的位置 */task_stacks[task_count++] = (int)top;//栈位置记录在这里,第 task_count 个数组元素的值就是指针指向的地址 int 值
}

psr 的值可以通过查表查看,比如规定是 thumb 还是 arm 的 isa 标志位。

传入参数要注意,栈参数 stack 一定要各不相同,这样才能保证各个任务栈互不干扰,

启动任务

现在每个任务都可以有一个栈了。

启动任务:把这个任务的栈中寄存器值,也就是上次停止运行时的状态恢复。

我们可以在定时器中断中切换任务,这就是时间片轮询的实现。

首先,这个任务想切换,得先创建好吧?没创建切换个锤。于是我们可以用一个变量 is_task_running 规定任务是否创建完成,定时器中断处理里如果发现没启动就先不做切换操作。

然后,如果任务都创建完成了,就在定时器中断处理中修改当前任务的 flag 值来不断切换应该运行的任务。

void SysTick_Handler(int lr_bak, int old_sp)
{int stack;int pre_task;int new_task;SCB_Type * SCB = (SCB_Type *)SCB_BASE_ADDR;/* clear exception status */SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;/* 如果还没有创建好任务, 直接返回 */if (!is_task_running()){return;  // 表示无需切换}/* 启动第1个任务或者切换任务 */if (cur_task == -1){/* 启动第1个任务 */cur_task = 0;/* 从栈里恢复寄存器 *//* 写汇编 */stack = get_stack(cur_task);StartTask_asm(stack, lr_bak);return ; /* 绝对不会运行到这里 */}else{/* 切换任务 */// 取出下一个任务pre_task = cur_task;new_task = get_next_task();if (pre_task != new_task){			/* 保存 pre_task: 在汇编里已经保存了 *//* 更新sp */set_task_stack(pre_task, old_sp);/* 切换 new_task */stack = get_stack(new_task);cur_task = new_task;StartTask_asm(stack, lr_bak);}}}

具体的恢复栈实现,我们需要操作汇编。我们查询栈数组找到当前应该恢复的栈的 int 值,这个值作为 sp,栈指针。

StartTask_asm PROC; 从任务的栈里把R4~R11读出来写入寄存器; r0 : 保存有任务的栈				; r1 : 保存有LR(特殊值)LDMIA r0!, { r4 - r11 }	;从 r0 开始读取一系列数据,边读边自增; 更新SPMSR MSP, R0;MOV SP, R0,因为栈操作只能用 msr; 触发硬件中断返回: 它会把栈里其他值读出来写入寄存器(R0,R1,R2,R3,R12,PSR)BX R1ENDPSysTick_Handler_asm PROC; 在这里保存R4~R11STMDB sp!, { r4 - r11 }STMDB sp!, { lr }MOV R0, LR ; LR是一个特殊值ADD R1, SP, #4BL SysTick_Handler  ; 这个C函数保证不破坏R4~R11LDMIA sp!, { r0 }LDMIA sp!, { r4 - r11 }BX R0ENDPEND

中断函数的返回地址是跳到一个处理函数,来处理剩余的硬件要处理的寄存器.

发生中断 -> 系统自动跳到 SysTick_Handler_asm (当发生中断自动跳转的时候,此时 lr 是一个特殊值,是硬件恢复现场的地址。中断程序执行完毕返回的时候应当跳转到这里,触发硬件的恢复工作)-> 存储 R4-R11,lr -> 把 lr 和 sp+4 作为参数,开始调用 SysTick_Handler 函数 -> 获取新任务的栈空间 -> 调用 StartTask_asm,获取其数组栈中保存的数据 -> 跳转到 Task a,开始执行 -> 中断恢复。

中断恢复:当跳转到 SysTick_Handler 函数时,此时 lr = 一个特殊值(如 0xFFFFFFF9),当程序跳转到这里的时候就会执行中断的返回,触发硬件的恢复工作。

graph TB
A[发生中断]-->B[硬件压栈,如 PC,LR,SP 等]
B-->C[SysTick_Handler_asm]
C-->D[SysTick_Handler]
D-->E{当前是否有任务执行}
E--是-->F[保存当前任务现场]
F-->G
E--否-->G[获取新任务栈]
G-->H[StartTask_asm]
H-->I[恢复 R4-R11]
I-->J[跳转到硬件中断恢复现场处]

main 创建的两个任务各自的栈是这样定义的:

static char stack_a[1024] __attribute__ ((aligned (4)));;
static char stack_b[1024] __attribute__ ((aligned (4)));;

意为数组元素以4字节为长度对齐。因为字长32,存储寄存器状态也是4字节。

切换任务

保存A现场;取出B栈;切换任务。

在启动任务的基础上加任务切换保存旧任务的代码。

内容见上。

补充内容

  1. 如果需要加数组的不同状态(锁定等),则可以定义一个状态数组,我们访问数组来判断当前这个任务能不能被切换。
  2. 同步互斥:为了进行原子操作,可以暂时关闭中断避免多个任务同时操作一个变量造成错误。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/150585.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

102-视频与网络应用篇-环境搭建

1.开发环境 本栏目开发所采集的操作系统是windows10Vmware上安装的Ubuntu18&#xff0c;关于Vmware和Ubuntu操作系统的安装过程本文不详细描述&#xff0c;具体安装步骤大家可以自己百度&#xff0c;有很丰富的安装教程。 本栏目查看资料、代码编辑、均在windows端&#xff0c;…

【文末送书】Python数据分析

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

基于树种优化的BP神经网络(分类应用) - 附代码

基于树种优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于树种优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.树种优化BP神经网络3.1 BP神经网络参数设置3.2 树种算法应用 4.测试结果&#xff1a;5.M…

短视频矩阵系统源码--源头技术独立自研框架开发

目录 一、批量剪辑&#xff08;采用php语言&#xff0c;数学建模&#xff09; 短视频合成批量剪辑的算法主要有以下几种&#xff1a; 1. 帧间插值算法&#xff1a;通过对多个视频的帧进行插帧处理&#xff0c;从而合成一段平滑的短视频。 2. 特征提取算法&#xff1a;提取多…

CC++内存管理

目录 C/C内存分布 C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free C内存管理方式 new和delete的使用 new和delete底层原理 operator new与operator delete函数 new和delete的实现原理 定位new表达式(placement-new) C/C内存分布 1. 栈 又叫堆栈…

给 Linux0.11 添加网络通信功能 (Day1: 确认 qemu-system-i386 提供了虚拟网卡)

感觉单纯读闪客的文章&#xff0c;以及读 Linux0.11 源码&#xff0c;而不亲自动手做点什么&#xff0c;很难学会&#xff0c;还是得写代码 定个大目标&#xff1a;给 Linux0.11 添加网络通信功能 今日的小目标&#xff1a;先确认 qemu-system-i386 提供了网卡功能 here we …

古记事法:Windows 下 16 位汇编环境搭建指南(DOSBox-X 篇)

文章目录 参考环境DOSBox-XWOWWindows On Windows 产生的原因Windows On Windows 的工作原理WOW16 的结束与 WOW64 的未来 在现代操作系统中运行 16 位应用程序DOSBox-X 16 位汇编环境的搭建应用准备挂载自动挂载dosbox-x.conf配置工具 参考 项目描述搜索引擎Bing、GoogleAI 大…

基于风驱动优化的BP神经网络(分类应用) - 附代码

基于风驱动优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于风驱动优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.风驱动优化BP神经网络3.1 BP神经网络参数设置3.2 风驱动算法应用 4.测试结果&#x…

数据结构——AVL树(详解 + C++模拟实现)

文章目录 前言AVL树的概念AVL树节点的定义AVL树类框架AVL树的插入AVL树的旋转新节点插入较高子树的左侧 —— 左左: 右单旋新节点插入较高右子树的右侧——右右: 左单旋新节点插入较高左子树的右侧 —— 左右&#xff1a; 先左单旋然后再有单旋新节点插入较高右子树的左侧&…

1500*B. Zero Array(贪心数学找规律)

Problem - 1201B - Codeforces 解析&#xff1a; 因为每次减少2&#xff0c;如果总和为奇数肯定无法实现。 特例&#xff0c;如果某个数大于其他所有数的总和&#xff0c;同样无法实现。 其他均可实现。 #include<bits/stdc.h> using namespace std; #define int long l…

基于生物地理学优化的BP神经网络(分类应用) - 附代码

基于生物地理学优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于生物地理学优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.生物地理学优化BP神经网络3.1 BP神经网络参数设置3.2 生物地理学算法应用 4…

PyQt5+Qt设计师初探

在上一篇文章中我们搭建好了PyQt5的开发环境&#xff0c;打铁到趁热我们基于搭建好的环境来简单实战一把 一&#xff1a;PyQt5包模块简介 PyQt5包括的主要模块如下。 QtCore模块——涵盖了包的核心的非GUI功能&#xff0c;此模块被用于处理程序中涉及的时间、文件、目录、数…

【Spring Cloud】基于 Feign 实现远程调用,深入探索 Feign 的自定义配置、性能优化以及最佳实践方案

前言 在微服务架构中&#xff0c;服务之间的通信是至关重要的&#xff0c;而远程调用则成为实现这种通信的一种常见方式。在 Java 中&#xff0c;使用 RestTemplate 是一种传统的远程调用方式&#xff0c;但它存在一些问题&#xff0c;如代码可读性差、编程体验不一致以及参数…

基于水循环优化的BP神经网络(分类应用) - 附代码

基于水循环优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于水循环优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.水循环优化BP神经网络3.1 BP神经网络参数设置3.2 水循环算法应用 4.测试结果&#x…

使用css制作3D盒子,目的是把盒子并列制作成3D货架

注意事项&#xff1a;这个正方体的其他面的角度很难调&#xff0c;因此如果想动态生成&#xff0c;需要很复杂的设置动态的角度&#xff0c;反正我是折腾了半天没继续搞下去&#xff0c; 1. 首先看效果&#xff08;第一个五颜六色的是透明多个面&#xff0c;第2-3都是只有3个面…

PageRank(下):数据分析 | 数据挖掘 | 十大算法之一

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

云服务器CVM_云主机_云计算服务器_弹性云服务器-腾讯云

腾讯云服务器CVM提供安全可靠的弹性计算服务&#xff0c;腾讯云明星级云服务器&#xff0c;弹性计算实时扩展或缩减计算资源&#xff0c;支持包年包月、按量计费和竞价实例计费模式&#xff0c;CVM提供多种CPU、内存、硬盘和带宽可以灵活调整的实例规格&#xff0c;提供9个9的数…

linearlayout中使用多个weight导致部分子控件消失异常

问题描述&#xff1a; 在一个linearlayout中写了两个用到weight的布局&#xff0c;在androidstudio中显示正常 但是代码跑起来之后最下面哪一行都消失了&#xff1b; 解决办法1 把两个用到weight的改成一个了&#xff0c;外面那层的weight写成固定宽度就能正常显示出丢失的…

二叉树--翻转二叉树

文章前言&#xff1a;如果有小白同学还是对于二叉树不太清楚&#xff0c;作者推荐&#xff1a;二叉树的初步认识_加瓦不加班的博客-CSDN博客 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 如果思路不清楚&#xff0c;请看动态页面&am…

【Spring内容介绍 | 第一篇】什么是事务管理

前言&#xff1a; 当今软件开发行业中&#xff0c;事务管理是一个不可或缺的重要组成部分。随着企业应用的复杂性和数据交互的增加&#xff0c;确保数据的一致性和完整性变得越来越关键。Spring框架作为一种全功能的应用程序开发框架&#xff0c;为我们提供了强大而灵活的事务管…