来自Colin Walls的关于RTOS的全部真相。 第4条 任务,上下文切换和中断

任务标识符


您必须能够识别系统中的每个任务。 此要求对其他内核对象很重要,但是与本文主题相对应的任务中有一些细微差别。



RTOS开发人员使用不同的方法来识别任务,但是可以区分四种通用策略:

  • 使用指向其“控制块”的指针来识别任务。 指针始终是唯一的,并且使用起来也很方便,因为许多API调用都需要访问控制单元。 这意味着所有任务数据都存储在随机存取存储器(RAM)中,这可能效率很低。 指针通常占用大约32位内存。
  • 可以使用任意的“索引号”定义任务。 在授予对某些表中的记录的访问权限时,此值可能会派上用场。 根据RTOS支持的任务数量的限制,这样的标识符可以占用八位或更少的内存。
  • 一些实时操作系统每个优先级只允许一个任务,因此使用优先级来唯一地标识一项任务。 这意味着无法更改任务的优先级。 此方法是先前方法的变体。
  • 任务的名称可以是字符串。 这对于调试很有用,但不可能是唯一标识任务的有效方法。 支持任务命名的RTOS通常具有API调用等使用的附加标识符(例如指针)。 一个好的调试器允许您在主机上本地调用它们。

该系列中的先前文章:
第3条 任务与计划
第2条。 RTOS:结构和实时模式
第1条 RTOS:简介。

上下文切换


上下文切换是将控制权从一个任务转移到另一任务的过程。 由于上下文切换的工作原理是RTOS的基本原理,因此值得更深入地探讨这个主题。

什么是任务?


我们知道一个任务是一个准独立程序,它在RTOS的控制下与许多其他任务共享处理器时间。 但是您需要考虑什么才是任务的真正特征。

寄存器集


任务最终是一组唯一的处理器寄存器值。 它们要么被加载到处理器寄存器中(即任务是当前的),要么被存储在预定的执行时间之前。 在理想情况下,中央处理器将具有几组寄存器,并且每组都可以分配给一个单独的任务。 这已在特殊场合实施。 多年前,德州仪器(TI)的TI 9900系列为每项工作提供了许多寄存器集,但是它们是在主存储器中实现的,从而限制了性能。 SPARC体系结构(以前在Unix桌面系统上使用)在“环形结构”中支持许多寄存器集,但是集的数量仍然有限。

内部数据


任务可能会有自己的堆栈,可以为每个任务单独设置堆栈的大小,也可以将其作为系统中所有任务的全局参数。 它与寄存器一起为特定任务提供数据存储。 内存中可能还存在其他区域,用于存储特定任务的数据。

共享资源


几乎所有资源都可以在任务之间共享。 该代码可以是通用的:某些功能或整个任务代码。 必须确保代码是可重入的,首先,不应使用静态变量(指定为静态变量或仅在函数外部使用)。 小心非内置的标准库模块; 它们通常具有许多不可靠的功能。

数据共享也是可能的,但是必须进行仔细的访问控制。 理想情况下,在任何给定时间,只有一个任务是数据的“所有者”。

如何保持上下文


重新安排任务的时间(即不再是当前任务)时,需要将其寄存器集保存在某处。 至少有两种可能性:

  • 寄存器可以存储在用于任务的特殊表中。 可能是任务控制块(TCB)的一部分。 大小是一个可预测且恒定的值(对于特定的CPU架构)。
  • 寄存器可以被压入任务堆栈。 这需要分配足够的额外堆栈空间并存储指针(可能在TCB中)。

机制的选择取决于特定RTOS的功能以及目标处理器。 一些(通常是32位)设备可以有效地访问堆栈; 使用表时,其余的(例如8位)可能会更理想。

动态任务创建


RTOS体系结构的主要方面是RTOS是“静态”或“动态”的。

当使用静态RTOS时,一切都在构建应用程序期间确定,尤其是系统中的任务数。 对于通常具有有限功能的嵌入式应用程序,这是一个逻辑解决方案。

动态RTOS启动一个任务(可以是专门的“主”任务),并根据需要创建和删除其他任务。 这样可以使系统适应不断变化的要求,并且与台式机系统更接近,后者以这种方式运行。 静态/动态视图也适用于其他内核对象。

动态任务创建要求


大多数商业RTOS中都包含此功能。 但是,只有一小部分应用程序确实需要动态操作模式。 通常,系统会启动,创建所有必要的任务(和其他对象),然后再也不会创建和销毁应用程序代码。 创建动态任务的能力已成为一种形式。 一个供应商介绍了它,所有其他供应商也效仿了。

值得注意的是,OSEK / VDX标准需要静态体系结构,即使这可能适用于相当复杂的应用程序。 这些要求的结果是无法使用包装器(常规(动态)RTOS上的中间层)实施OSEK / VDX。

动态任务创建的陷阱


动态操作模式存在多个令人困扰的问题。

首先,系统变得更加复杂,这意味着对于描述任务(TCB)的数据结构,需要附加信息。 通常,它们以双向列表的形式实现,这导致与内存量相关的成本。
描述任务的所有数据必须存储在RAM中。 这是低效的,因为它们中的大多数可能只是从ROM复制的持久数据元素。 此外,在较低级别的处理器(微控制器)上,可能无法获得RAM。

可能最令人担忧的是可能出现不可预知的资源短缺,这可能导致无法创建新对象。 由于实时系统的本质是其可预测性,因此这是不可接受的。 因此,在使用动态任务(和其他对象)创建时必须小心。

打断


可以在不使用中断的情况下实现嵌入式实时系统,但这不是典型的情况。

中断和内核


使用RTOS时,将使中断处理程序(ISR)尽可能容易地“窃取”计划任务中的最少处理器时间。 通常,可以简单地对设备进行维护,并且任何需要执行的任务都将排队等待处理。 另外,仅仅因为中断变化很大,通常很难谈论中断及其与内核的交互。 一方面,RTOS开发人员可以确保中断根本不与内核相关,并且程序员必须使用ISR中的大量处理器时间来确保任务调度程序不会过载。 另一方面,RTOS可以完全控制整个中断子系统。 所描述的方法没有对与错,只是不同而已。

保存上下文


ISR始终需要保持“上下文”,以使可中断代码不受ISR计算的影响。 在没有RTOS的系统中,只需保存ISR使用的所有寄存器(通常在堆栈上)并在返回之前恢复它们即可。 一些处理器具有专用的ISR堆栈,而其他一些处理器仅使用与应用程序代码相同的堆栈。

使用RTOS时,方法可能完全相同。 同样,ISR使用的堆栈可以从当前任务中“借用”,也可以是为中断分配的另一个堆栈。 即使处理器本身不支持中断堆栈,某些内核也会实现此功能。 如果ISR进行会影响任务计划程序的API调用,则情况会很复杂。 这可能导致中断从发生中断时启动的任务返回到另一任务。

中断与调度


在多种情况下,ISR执行代码可能会返回到另一个任务:

  • 如果使用优先级任务计划程序,则ISR可以为已经完成的任务分配更高的优先级,而不是为当前任务分配更高的优先级。
  • ISR可能会暂停当前任务。
  • 使用时间片调度程序(TS),系统计时器中断处理程序将控制时间间隔,并在必要时可以调用调度程序。

时钟计时器(滴答时钟)


在嵌入式系统中,经常发现使用定期的“时钟计时器”(时间片)。 在某些RTOS中,它是必需的组件。 通常,时钟计时器的存在是可选的,并且时钟计时器的缺失仅会阻止某些服务的可用性。 计时器中断处理程序通常提供四种功能:

  • 如果使用了时隙调度程序,则计时器中断处理程序将控制时间计数器,并在每次时间用完时调度新任务。
  • 提供系统时间支持。 这主要是一个32位变量,由计时器递增,可以由任务预定义或请求。
  • 如果RTOS为应用程序提供了计时器,则计时器中断处理程序将支持它,该中断处理程序将负责到期和重新安排。
  • 如果RTOS在阻止API调用中支持超时,或者任务可能处于睡眠状态,则计时器中断处理程序将支持这些时间间隔。

当我们开发自己的实时OSRV MAX操作系统时(此前已发表过有关此文章的文章),我们的团队“遇到了” Mentor Graphics的微电子学和固件专家Colin Walls的博客。 文章似乎很有趣,可以自己翻译,但是为了不“写到桌子上”,他们决定发表文章。 希望它们对您也有用。 如果是这样,那么我们计划发布该系列中所有翻译的文章。

关于作者:Colin Walls在电子行业工作了30多年,大部分时间用于固件。 他现在是Mentor Embedded(Mentor Graphics的一个部门)的固件工程师。 Colin Walls经常在会议和研讨会上发表演讲,他撰写了许多技术文章并撰写了两本有关固件的书。 居住在英国。 Colin的专业博客:blogs.mentor.com/colinwalls,电子邮件:colin_walls@mentor.com

阅读之前发布的系列的第一,第二篇第三篇文章。

Source: https://habr.com/ru/post/zh-CN415427/


All Articles