STM32F1与STM32CubeIDE编程实例ThreadX的相关概念
STM32F1与STM32CubeIDE编程实例-ThreadX的相关概念
Threadx中的相关概念
文章目录
在进一步介绍如何使用ThreadX之前,让我们先了解一下与ThreadX的线程相关的概念。
1、线程优先级
大多数实时嵌入式操作系统都会使用优先级来确定系统中线程的重要性。线程的优先级主要有两种类型:
- 静态优先级(Static Priority):在线程创建时分配,在线程执行过程,不会改变。
- 动态优先级(Dynamic Priority):动态优先级是在创建线程时分配的优先级,但可以在执行期间随时更改。 此外,对可以发生的优先级更改,理论上数量没有限制。
ThreadX 提供了一种灵活的动态优先级分配方法。在ThreadX中,每个线程都有自己的优先级,并对如何设置线程的优先级没有任何限制。在极端的情况,所有线程都分配到相同的且不会改变的优先级。但是在大多数情况下,线程的优先级是经过仔细的分配和可以修改的。线程的优先级只是为了反映在系统处理事情过程中重要性的变化。
ThreadX默认的优先级范围为[0~31]。其中,0代表最高优先级, 31代表最低优先级。
2、线程状态
ThreadX 维护多个内部数据结构来管理处于各种执行状态的线程。其中最重要的是Suspended Thread List(挂起线程列表)和Ready Thread List(就绪线程列表)。在Suspended Thread List中的线程状态是**Suspended(挂起)的,此时的线程运行。在Ready Thread List中的线程处于Ready(就绪)**状态,随时被ThreadX的线程调度器调度执行。
当一个线程被放入Suspended Thread List时,是因为一些事件或情况,例如被迫等待一个不可用的资源。 这样的线程将保留在该列表中,直到该事件或情况得到解决。 当线程从挂起线程列表中删除时,会发生两种可能的操作之一:将其置于就绪线程列表中,或者终止。
当一个线程准备好执行时,它被放置在就绪线程列表中。 当 ThreadX 调度一个线程执行时,它会选择并删除该列表中具有最高优先级的线程。 如果列表中的所有线程具有相同的优先级,ThreadX 将选择等待时间最长的线程。
如果由于某种原因一个线程没有准备好执行,它会被放置在暂停线程列表中。例如,如果一个线程正在等待资源,如果它处于**Sleep(休眠)**模式;如果它是使用 TX_DONT_START 选项创建的,或者如果它被显式挂起,那么它将驻留在挂起线程列表中。
3、线程调度
3.1 抢占式、基于优先级的线程调度策略
不同的RTOS在不同的应用场景下,有不同的线程调度策略。ThreadX支持抢占式、基于优先级的线程调度策略。抢占式、基于优先级的调度是指一种调度类型,其中较高优先级的线程可以中断和挂起当前正在执行的具有较低优先级的线程。如下图所示,不同优先级的线程调度过程:

在上图示例中,Thread1 控制了处理器,执行线程代码。 但是,Thread2 具有更高的优先级并准备好执行。 ThreadX 然后中断 Thread1 并让 Thread2 控制处理器执行线程代码在。 当Thread2完成它的工作时,ThreadX 将控制权返回给Thread1,并从被中断的处恢复运行。
3.2 轮询调度策略
轮询调度是指一种调度算法,旨在在多个线程具有相同优先级的情况下提供处理器共享。轮询调度有两种方法实现,一种是基于时间片(Time Slice),另一种是基于**协作多线程(Cooperative Multithreading)**方法,ThreadX对这两种轮询调度策略均支持。

基于时间片的轮询调度策略为每个线程分配了均等控制CPU的机会,因此,轮询调度策略中,不存在线程竞争、死锁等情况。
上图演示了轮询调度策略的时间片方法,其中 Thread1 执行指定的时间片,然后是 Thread2,然后是 Thread3,以此类推到 Threadn,之后该过程重复。
循环调度的第二种方法是通过使用由当前正在执行的线程进行的协作调用来实现的,该调用暂时放弃对处理器的控制,从而允许执行具有相同或更高优先级的其他线程。 这种方法有时称为**协作多线程(Cooperative Multithreading)**。如下图所示:

在协作多线程中,当一个正在执行的线程放弃对处理器的控制时,它被放置在就绪线程列表的末尾,如上图中的阴影线程所示。 然后执行列表前面的线程,然后执行列表中的下一个线程,依此类推,直到阴影线程位于列表的前面。上图只显示了具有相同优先级的就绪线程。 但是,就绪线程列表可以保存具有多个不同优先级的线程。 在这种情况下,调度程序会将其注意力限制在具有最高优先级的线程上。
总之,协作多线程特性允许当前执行的线程自愿放弃对处理器的控制。 然后该线程被放置在就绪线程列表中,并且在处理完所有其他具有相同(或更高)优先级的线程之后,它将无法访问处理器。
4、确定性(Determinism)
实时嵌入式系统的一个重要特征是**确定性(Determinism)**。确定性的传统定义基于以下假设:对于每个系统状态和每组输入,可以确定一组唯一的输出和系统的下一个状态。 然而,我们通过要求处理任何任务所需的时间是可预测的,来加强对实时嵌入式系统的确定性的定义。 特别是,我们不太关心平均响应时间,而不是最坏情况的响应时间。 例如,我们必须能够保证每个系统调用的最坏情况响应时间,以使实时嵌入式系统具有确定性。 换言之,仅仅获得正确答案是不够的。 我们必须在规定的时间范围内得到正确的答案。
许多RTOS 供应商声称他们的系统是确定性的,并通过发布每个系统调用所需的最小、平均和最大时钟周期数表来证明这一断言的合理性。 因此,对于确定性系统中的给定应用程序,可以计算给定线程数的时间,并确定该应用程序是否实际可能实现实时性能。
5、内核(Kernel)
内核是 RTOS 的最小实现。 它通常至少由一个调度程序和一个上下文切换处理程序组成。 大多数现代商业 RTOS 实际上是内核,而不是成熟的操作系统。
6、RTOS
RTOS 是专用于控制硬件的操作系统,必须在指定的时间限制内运行。 大多数 RTOS 用于嵌入式系统。
7、Context 切换(Context Switch)
Context是线程的当前执行状态。 通常,它由程序计数器、寄存器和堆栈指针等项目组成。 Context切换是指保存一个线程的上下文并恢复另一个线程的Context以便它可以执行。
Context通常是由于抢占、中断处理、时间片、协作循环调度或线程因为需要不可用资源而暂停的结果。 当一个线程的Context恢复时,线程会在它停止的地方恢复执行。 内核执行Context切换操作, 执行Context切换所需的实际代码必然是特定于处理器的。
8、时间片(Time Slice)
线程在放弃处理器之前执行的时间长度(即计时器滴答数)称为其时间片。
当 ThreadX 中一个线程的(可选)时间片到期时,所有其他具有相同或更高优先级的线程都有机会在时间片线程再次执行之前执行。
时间片提供了另一种形式的轮询调度。
ThreadX 在每个线程的基础上提供可选的时间片。 线程的时间片是在创建期间分配的,并且可以在执行期间进行修改。 如果时间片太短,那么调度器将浪费太多的处理时间来执行上下文切换。 但是,如果时间片太长,那么其他线程可能不会得到调度。
9、中断处理(Interrupt Handling)
实时嵌入式应用程序的一个基本要求是能够对异步事件(例如硬件或软件中断)提供快速响应。 当中断发生时,正在执行的线程的上下文被保存并且控制被转移到适当的中断向量。 中断向量是中断服务程序 (ISR) 的地址,它是用户编写的软件,旨在处理或服务特定中断的需求。 可能有许多 ISR,具体取决于需要处理的中断数量。 服务中断所需的实际代码必然是特定于处理器的。
10、线程饥饿(Thread Starvation)
抢占式、基于优先级的调度的一种异常是线程饥饿。 在这种情况下,具有较低优先级的线程很少被执行,因为处理器将大部分时间都花在了较高优先级的线程上。 缓解此问题的一种方法是确保较高优先级的线程不会独占处理器。 另一种解决方案是逐渐提高饥饿线程的优先级,以便它们有机会执行。
11 、优先级反转(Priority Inversion)
当具有不同优先级的两个线程共享一个公共资源时,可能会发生不希望的情况。 优先级反转就是这样一种情况; 当一个较高优先级的线程因为较低优先级的线程获得了较高优先级线程所需的资源而被挂起时,就会出现这种情况。 当更高优先级的线程正在等待时共享资源未使用时,问题会更加复杂。 这种现象可能会导致优先级反转时间变得不确定并导致应用程序失败。如下图所示:

在上图中,Thread3(具有最低优先级)准备就绪。 它获得互斥量并开始执行。 一段时间后,Thread2(具有更高优先级)准备就绪,抢占 Thread3,并开始执行。 然后 Thread1(具有最高优先级)准备就绪。 但是,它需要 Thread3 拥有的 mutex M,因此它会被挂起,直到 mutex M 可用。 因此,高优先级线程(即 Thread1)必须等待低优先级线程(即 Thread2)才能继续。在此等待期间,mutex M 保护的资源未被使用,因为 Thread3 已被Thread2抢占。
12、优先级继承(Priority Inheritance)
优先级继承是 ThreadX 提供的一项可选功能,仅用于互斥体(Mutex)服务。 优先级继承允许低优先级线程临时承担高优先级线程的优先级,该线程正在等待低优先级线程拥有的互斥锁。优先级继承功能通过消除中间线程优先级的抢占,帮助应用程序避免不确定的优先级反转。
13、抢占阈值(Preemption-Threshold)
Preemption-Threshold 是 ThreadX 独有的功能。 创建线程时,开发者可以选择指定一个优先级上限来禁用抢占。这意味着优先级大于指定上限的线程仍然允许抢占,但优先级等于或小于上限的线程不允许允许抢占该线程。 可以在线程执行期间随时修改抢占阈值。 如下图所示,它说明了抢占阈值的影响。

在上图中,创建了一个线程,并为其分配了 20 的优先级值和 15 的抢占阈值。因此,只有优先级高于 15(即 0 到 14)的线程才被允许抢占该线程。 即使优先级 15 到 19 高于线程的优先级 20,具有这些优先级的线程也不会被允许抢占该线程。
14、ThreadX相关文章
文章来源: https://iotsmart.blog.csdn.net/article/details/125083300