你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

抢占式操作系统(大多数RTOS)的抢占原理

[复制链接]
gaosmile 发布时间:2020-11-18 20:08
操作系统对于初学者来说是一个很神秘的东西,里面很多原理摸不清楚,导致还没入门就放弃了。
本文就来从底层开始,详细讲述抢占式操作系统(大多数RTOS)的抢占原理。
包含:
  • 线程基础原理
  • 线程之间抢占
  • 中断抢占线程
  • 中断抢占中断


strongerHuang
1
运行多个线程

1.单核“单线程”
严格来说,单核处理器一次只能执行一条指令,也就是说只能“单线程”。(当然,多核处理器就不一样)

为了在单核处理器上运行多个线程,我们实际上需要定期在线程之间进行快速切换,以便用户感觉多个线程在并行运行。

比如处理器执行两个线程,处理器实际在两个线程之间来回切换,如下图:
微信图片_20201118200459.png

2.处理器在线程之间切换,它是如何做到的?

我们说的单核处理器是“单线程”的,它有一组寄存器,我们就叫这组寄存器属于一个“线程”。

例如,计算两个数字的总和时:

  1. //假设我们有两个整数:a和b
  2. int c = a + b ;
复制代码

实际发生的情况如下所示(当然,它取决于的MCU类型,但总体思路是相同):

  1. # MIPS反汇编:

  2. LW V0, -32744(GP)   # "a" 的值从RAM加载到寄存器V0
  3. LW V1, -32740(GP)   # 值"b" 从RAM加载到寄存器V1
  4. ADDU V0, V1, V0     # a、b值相加,结果保存到寄存器V0中
  5. SW V0, -32496(GP)   # 寄存器V0的值存储在RAM中(变量c所在的位置)
复制代码

你会发现上面执行了4个动作,但是抢占式操作系统可以在任何时候抢占另一个线程,包括在这4个动作之间。

假如在这过程中有其他线程抢占了,其他线程同样抢占了当前线程V0、 V1,如果不对V0、 V1进行保存,那么下次回来执行当前线程,结果就会出错。

所以,针对当前这种问题,我们就需要在切换线程之前,对V0、 V1的数值进行保存,当下次切换到当前线程,再恢复V0、 V1的数值,大致流程如下:
微信图片_20201118200503.png

大概意思就是:当我们需要从一个线程切换到另一个线程时,内核获得控制权,执行必要的内务处理(至少要保存和恢复寄存器值),然后将控制权转移到下一个线程以运行。

strongerHuang
2
线程的堆栈

上面说的抢占位置,到底在哪里,每个线程保存在哪个寄存器值中?这就是线程的堆栈的内容。

在有MMU的操作系统中,(用户的)线程堆栈可以按需动态增长:线程需要的堆栈空间越多,线程堆栈就越多(如果内核允许)
但是,我们一般的MCU却没有MMU这个“高端”的东西,所有RAM都静态映射到地址空间。因此,每个线程都会有用于堆栈的RAM空间,如果线程使用的RAM超过堆栈的数量,则会导致内存溢出或细微的错误。(实际上,每个线程的堆栈空间只是一连续数组空间)。
因此,当我们决定为每个线程分配多少堆栈时,我们只是估计可能需要多少堆栈,但是具体多少可能不是很清楚。
比如,如果这是一个具有多层嵌套调用的GUI线程,则可能需要数个千字节,但如果它是一个流水灯的小线程,则可能几十字节就足够了。
假设我们有三个线程,它们的堆栈消耗如下:
微信图片_20201118200507.png


如上面所述,每个线程的寄存器值都保存在线程的堆栈中。线程的寄存器值集称为线程的“上下文”。如下图所示(线程A为在正在执行的“活动线程”):
微信图片_20201118200510.png

请注意,在正在执行的线程A的上下文没有保存在堆栈中,堆栈指针指向线程A用户数据的顶部,并且当前处理器的寄存器专用于线程A。
当内核决定将控制权切换到线程B时,它将执行以下操作:
  • 将所有寄存器值保存到堆栈中(保存到线程A堆栈的顶部);
  • 将堆栈指针切换到线程B的堆栈顶部;
  • 从堆栈(从线程B的堆栈顶部)恢复所有寄存器值;


此时,你会看到:
微信图片_20201118200513.png

strongerHuang
3
中断(ISR)抢占

上面在执行过程中,或进行上下文切换时,还可能会涉及到一个非常重要的内容:中断
MCU通常具有外设:TIM、UART、 SPI、 CAN等,它们随时都能发生重要事件以触发中断。
中断条件是当当前正在执行的线程暂停时,处理器在一段时间内执行其他操作(Handles Interrupt),然后返回。中断可能随时触发,我们应该做好处理的准备。
中断处理程序称为ISR(中断服务程序):中断可能具有不同的优先级,例如,如果触发了一些低优先级的中断,则当前正在执行的线程将暂停,并且ISR会获得控制权。然后,如果触发了某个高优先级中断,则当前正在执行的ISR将再次暂停,并为该高优先级中断运行一个新的ISR。
这样一来,完成后,控制权将返回到第一个ISR,并且在完成时,也会恢复被中断的线程。
重要的关键代码:在线程活跃过程中,如果有重要的事情“关键的代码”,在这过程中如果中断发生,很容易导致意想不到的结果。
这部分关键的代码,我们需要要保护起来,通常我们的做法就是:在之前“关键代码”之前禁用全局中断,执行完之后,开始全局中断。

有点需要注意:关闭全局中断,此时就不会相应中断,所以,“关键代码”不能太长。
strongerHuang
3
中断堆栈

在上面说到一点,高优先级中断抢占低优先中断,就会出现一个问题:低优先级的代码需要和线程一样,用于保存数据的堆栈。
一般有两种方法:
  • 使用被中断的线程堆栈;
  • 为中断使用单独的堆栈空间;


1.使用被中断的线程堆栈如果使用被中断的线程堆栈,就类似如下图:
微信图片_20201118200516.png

这种情况存在你一个严重的问题,你知道是什么吗?
频繁中断,或者中断较多,线程自身的堆栈空间就会很快被使用完。
每个线程的堆栈都应该包含以下内容:
  • 线程自己的数据;
  • 线程的上下文;
  • 用于执行最坏情况的ISR的数据。


因此,我们就需要换一种方法,为为所有ISR中断开辟单独的堆栈空间。
2.为中断使用单独的堆栈空间
微信图片_20201118200520.png

为中断使用单独的堆栈空间大致如上图所示。
好了,本文讲述了上面几种关于抢占,以及相关的内容,你学会了几点,又有几点没懂,欢迎留言讨论。
收藏 评论0 发布时间:2020-11-18 20:08

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版