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

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

[复制链接]
gaosmile 发布时间:2020-11-18 20:08
操作系统对于初学者来说是一个很神秘的东西,里面很多原理摸不清楚,导致还没入门就放弃了。! ~8 s7 R4 s% `7 f; W
本文就来从底层开始,详细讲述抢占式操作系统(大多数RTOS)的抢占原理。
# t- @; @) O& r+ Q% Z
包含:
  • 线程基础原理! y4 ?# n* p( o
  • 线程之间抢占2 j5 ?  K: G  N1 m7 W
  • 中断抢占线程
    ' W2 ]2 K  y5 k, w
  • 中断抢占中断
    : T: U+ z( j. n8 B3 X
, K# g; S, K& K) z! P! m
strongerHuang
1
运行多个线程

9 _- m$ \& Y9 W& k
1.单核“单线程”
严格来说,单核处理器一次只能执行一条指令,也就是说只能“单线程”。(当然,多核处理器就不一样)

2 s9 b, `4 Z1 T$ R7 o, Y2 {; T
为了在单核处理器上运行多个线程,我们实际上需要定期在线程之间进行快速切换,以便用户感觉多个线程在并行运行。
* ~. G' V( E1 @+ A: I
比如处理器执行两个线程,处理器实际在两个线程之间来回切换,如下图:
微信图片_20201118200459.png

; e1 ^. a" o5 V7 {3 u( |0 k
2.处理器在线程之间切换,它是如何做到的?
% Q- J) p. `! b, @7 s" H! C
我们说的单核处理器是“单线程”的,它有一组寄存器,我们就叫这组寄存器属于一个“线程”。

. `" i8 A, F& x) P
例如,计算两个数字的总和时:
  1. & `3 K, W& w; `  D3 z7 l  g9 H8 D& Q4 r' N
  2. //假设我们有两个整数:a和b
    , s, X$ p0 ~. g# S1 `. g
  3. int c = a + b ;
复制代码

' J& v; u+ K/ ]* `/ P
实际发生的情况如下所示(当然,它取决于的MCU类型,但总体思路是相同):
  1.   v/ ^* ?9 ^2 e. U# \3 \: e, o
  2. # MIPS反汇编:
    , T# f/ u3 @7 z1 L& K- ]' n

  3. 5 N/ K, S3 F. g7 |4 k; Y, G
  4. LW V0, -32744(GP)   # "a" 的值从RAM加载到寄存器V0
    8 Q9 n4 }9 g- t
  5. LW V1, -32740(GP)   # 值"b" 从RAM加载到寄存器V1
    - }9 ?  @0 I  B# _- o
  6. ADDU V0, V1, V0     # a、b值相加,结果保存到寄存器V0中
    + c9 y- @1 L5 E1 `2 ~* X: @
  7. SW V0, -32496(GP)   # 寄存器V0的值存储在RAM中(变量c所在的位置)
复制代码

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

6 O. ^8 M* ~$ k9 d: f' w; f  }
假如在这过程中有其他线程抢占了,其他线程同样抢占了当前线程V0、 V1,如果不对V0、 V1进行保存,那么下次回来执行当前线程,结果就会出错。

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

5 W% ]2 u. T8 f. H3 }  e
大概意思就是:当我们需要从一个线程切换到另一个线程时,内核获得控制权,执行必要的内务处理(至少要保存和恢复寄存器值),然后将控制权转移到下一个线程以运行。
  q$ G* l5 L' S) V
strongerHuang
2
线程的堆栈

2 M7 ]) F' ~8 }, H
上面说的抢占位置,到底在哪里,每个线程保存在哪个寄存器值中?这就是线程的堆栈的内容。* N2 i7 a8 }, S% C
6 a9 }5 h( U3 L6 h$ e
在有MMU的操作系统中,(用户的)线程堆栈可以按需动态增长:线程需要的堆栈空间越多,线程堆栈就越多(如果内核允许)。
/ F1 i7 I, @( i0 N% d
但是,我们一般的MCU却没有MMU这个“高端”的东西,所有RAM都静态映射到地址空间。因此,每个线程都会有用于堆栈的RAM空间,如果线程使用的RAM超过堆栈的数量,则会导致内存溢出或细微的错误。(实际上,每个线程的堆栈空间只是一连续数组空间)。
2 b+ j5 u- b0 A
因此,当我们决定为每个线程分配多少堆栈时,我们只是估计可能需要多少堆栈,但是具体多少可能不是很清楚。
" Y! l5 e& \3 W/ M+ P& B+ p: g
比如,如果这是一个具有多层嵌套调用的GUI线程,则可能需要数个千字节,但如果它是一个流水灯的小线程,则可能几十字节就足够了。
5 g8 S+ f" b8 J, d" }
假设我们有三个线程,它们的堆栈消耗如下:
微信图片_20201118200507.png
& v" i* w/ E. m: a3 L+ X9 O# r
: f+ {# R6 ^6 O) h8 o# k
如上面所述,每个线程的寄存器值都保存在线程的堆栈中。线程的寄存器值集称为线程的“上下文”。如下图所示(线程A为在正在执行的“活动线程”):
微信图片_20201118200510.png

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

    / c) V" E; b7 T. y: @0 H) L( J. m
1 h' b% n+ m! L5 p% K7 n1 Y
此时,你会看到:
微信图片_20201118200513.png
* i+ ~' q' C* ^& R) p, _0 L2 A
strongerHuang
3
中断(ISR)抢占

5 t% [. G7 u+ h4 k
上面在执行过程中,或进行上下文切换时,还可能会涉及到一个非常重要的内容:中断。/ B6 s9 h9 U3 r4 L1 P
MCU通常具有外设:TIM、UART、 SPI、 CAN等,它们随时都能发生重要事件以触发中断。4 X# r, z: Q' @. q, S
中断条件是当当前正在执行的线程暂停时,处理器在一段时间内执行其他操作(Handles Interrupt),然后返回。中断可能随时触发,我们应该做好处理的准备。& H; J$ Z/ n7 w
中断处理程序称为ISR(中断服务程序):中断可能具有不同的优先级,例如,如果触发了一些低优先级的中断,则当前正在执行的线程将暂停,并且ISR会获得控制权。然后,如果触发了某个高优先级中断,则当前正在执行的ISR将再次暂停,并为该高优先级中断运行一个新的ISR。
$ w" b& ~/ z6 C! V3 z, i$ O/ b
这样一来,完成后,控制权将返回到第一个ISR,并且在完成时,也会恢复被中断的线程。( g$ D. r( B4 Q& s+ G* k1 i+ B
重要的关键代码:在线程活跃过程中,如果有重要的事情“关键的代码”,在这过程中如果中断发生,很容易导致意想不到的结果。+ h7 I8 q; {' r
这部分关键的代码,我们需要要保护起来,通常我们的做法就是:在之前“关键代码”之前禁用全局中断,执行完之后,开始全局中断。* h1 e) r4 @( o, |3 ~5 q, M1 a
3 r0 V/ E! w* K7 W9 B
有点需要注意:关闭全局中断,此时就不会相应中断,所以,“关键代码”不能太长。8 N# P9 l  |6 ~0 o( p+ F( H: A
strongerHuang
3
中断堆栈
3 ~+ q- ^% M' ^$ ]2 s, z
在上面说到一点,高优先级中断抢占低优先中断,就会出现一个问题:低优先级的代码需要和线程一样,用于保存数据的堆栈。
" t# l4 a1 A% h  ~% c: D3 J- N# B
一般有两种方法:
  • 使用被中断的线程堆栈;
  • 为中断使用单独的堆栈空间;

    - ?3 u; i; y: j- e# |, U
% _8 A' q- X1 O
1.使用被中断的线程堆栈如果使用被中断的线程堆栈,就类似如下图:
微信图片_20201118200516.png
2 b7 o8 p/ R9 B
这种情况存在你一个严重的问题,你知道是什么吗?4 h8 c1 ~! [" ]% j  ~8 i7 U
频繁中断,或者中断较多,线程自身的堆栈空间就会很快被使用完。3 O' P7 J. c5 _& @. ]
每个线程的堆栈都应该包含以下内容:
  • 线程自己的数据;
  • 线程的上下文;
  • 用于执行最坏情况的ISR的数据。

    9 q5 e' l" P/ C$ B9 P0 e+ f8 l$ N

# E7 R1 l3 w+ M) [, I
因此,我们就需要换一种方法,为为所有ISR中断开辟单独的堆栈空间。' t* c+ _( L: m  @
2.为中断使用单独的堆栈空间
微信图片_20201118200520.png

1 M9 V$ n  O9 [9 O( G+ K
为中断使用单独的堆栈空间大致如上图所示。
  j; x. P2 D2 f+ [1 P+ K! o) v
好了,本文讲述了上面几种关于抢占,以及相关的内容,你学会了几点,又有几点没懂,欢迎留言讨论。/ W$ M' X1 V/ Q: Z
赞 收藏 评论0 发布时间:2020-11-18 20:08

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版