RTAI:实时应用接口
在深入探讨 RTAI 之前,快速回顾一下实时的含义以及标准 Linux 与实时的关系是恰当的。“实时”一词可以有非常不同的含义,具体取决于受众和所讨论的应用性质。然而,计算机科学文献通常只定义两种类型:软实时系统和硬实时系统。
“软实时系统”的特点是能够执行任务,该任务平均而言是按照期望的计划执行的。视频显示就是一个很好的例子,偶尔丢失一帧不会导致任何可感知的系统降级,前提是平均性能保持在可接受的水平。虽然可以使用诸如插值之类的技术来补偿丢失的帧,但系统仍然是软实时系统,因为实际数据已丢失,并且插值帧表示的是派生数据而不是实际数据。
“硬实时系统”体现了有保证的定时,不能错过定时截止日期,并且必须具有有界延迟。由于截止日期永远不能错过,硬实时系统不能使用平均情况性能来补偿最坏情况性能。硬实时任务的一个例子是监控核反应堆中的传感器,核反应堆必须使用复杂的数字控制系统以避免灾难。RTAI 为 Linux 提供了这些必要的硬实时扩展,从而使其能够面向不能错过定时截止日期的应用。
RTAI 为 Linux 提供了硬实时扩展,但标准 Linux 支持 POSIX 1003.13 实时扩展,那么标准 Linux 的“实时”能力有何不足呢?
POSIX 1003.13 标准定义了“多用户实时系统配置文件”,该文件允许将“实时”进程锁定到内存中,以防止进程被分页到硬盘,并允许使用特殊的调度程序,以确保这些进程始终以可预测的顺序执行。
Linux 通过提供符合 POSIX 标准的内存锁定 (mlock)、特殊调度 (sched_setsched) 系统调用和 POSIX RT 信号来满足此标准。虽然这些功能确实提供了改进的确定性性能,但由此产生的任务不能定义为硬实时任务,因为软实时进程可能会被内核活动阻止。
标准 Linux 实时 POSIX API 和实时内核提供了截然不同的服务,尤其是在多用户和多任务应用中。清单 1 中显示的伪代码中的简单程序可以用来演示系统性能和围绕 POSIX 方法的问题。
该程序读取进入系统调用之前的绝对时间,该系统调用用于将进程挂起预定的时间间隔。接下来,程序读取系统调用返回后的绝对时间。如果系统负载很重,内核活动级别很高,则系统调用的返回将被该活动延迟,因此该延迟的大小由预期睡眠时间(即 100 毫秒)与实际睡眠时间之间的差值定义。
对于空闲的标准 Linux 系统,上述任务性能非常稳定,并且定时偏差非常低,在 Pentium II 300MHz 系统上,最坏情况下的偏差约为 100 微秒。
但是,如果在标准 Linux 系统上运行该程序,使用相同的 POSIX 方法,但同时进行 I/O 活动,则性能会急剧下降,因为标准 Linux 的功能不允许测试程序抢占 I/O 密集型应用。例如,当您编辑一个非常大的文件并导致硬盘活动的同时运行测试程序时,平均执行时间很快变得不可接受。这种无法抢占的能力导致测试程序遭受典型的 30 毫秒偏差,最坏情况下超过数百毫秒。因此,在多任务环境中,使用 POSIX 方法的标准 Linux 进程不能用于可靠的硬实时性能。
尽管 Linux 社区的成员已经讨论过在未来的内核中引入低延迟调度程序以增强软实时性能,但标准 Linux 仍然远不能满足硬实时任务所需的有保证的响应时间。
通过将相同的测试程序作为实时任务运行,可以很容易地看到实时 Linux 的性能优势,无论 I/O 活动如何,最坏情况下的偏差都小于 12.5 微秒。
除了实时内核的调度优势外,实时架构的效率还允许 Linux 在激进的任务迭代速率下运行。例如,当以 500 微秒的间隔作为 POSIX 实时任务运行此程序时,Linux 完全停止。将此程序作为实时任务运行会为 Linux 留下足够的资源以继续运行。
通用操作系统(如 Linux)的运行要求与硬实时系统的运行要求不同。这种差异导致了以下总结的其他问题,这些问题限制了标准 Linux 作为实时操作系统的潜力。
Linux 内核使用粗粒度同步,这允许内核任务在很长一段时间内独占访问某些数据。这会延迟任何需要访问相同数据的 POSIX 实时任务的执行。
Linux 不会在系统调用期间抢占任何任务的执行。如果一个低优先级进程正处于 fork 系统调用的中间,并且收到了视频显示进程的消息,那么不幸的是,即使其优先级较低,该消息也将被保留在队列中,直到调用完成。解决方案是在内核中添加抢占点,但这会产生减缓所有系统调用的有害影响。
Linux 使高优先级任务等待低优先级任务释放资源。例如,如果任何进程分配了最后一个网络缓冲区,而一个更高优先级的进程需要一个网络缓冲区来发送消息,则更高优先级的进程必须等待其他进程释放网络缓冲区后才能发送其消息。
即使在更高优先级的进程可以占用所有可用处理器时间的情况下,Linux 调度算法有时也会给最不重要和最友好的进程一个时间片。这是通用操作系统的产物,它确保后台维护进程;例如,即使更高优先级的进程能够使用所有可用的处理器时间,也可以清理日志文件并运行的进程。
Linux 会重新排序来自多个进程的请求,以更有效地使用硬件。例如,为了最大限度地减少磁头移动或提高错误恢复的机会,来自低优先级进程的硬盘块读取可能优先于来自高优先级进程的读取请求。
Linux 将批量操作以更有效地使用硬件。例如,当内存紧张时,Linux 不会一次释放一个页面,而是会遍历页面列表,尽可能多地清除页面,这将延迟所有进程的执行。这对于通用操作系统来说显然是可取的,但对于实时进程来说是不可取的。
实时和通用操作系统具有相互矛盾的设计要求。在一个系统中可取的效果在另一个系统中通常是有害的。
不幸的是,在同一个内核中尝试同时满足这两个要求通常会导致一个系统两方面都做得不好。但这并不是说不能同时实现通用功能和实时确定性。事实上,现在存在结合这些要求的操作系统,它们确实是确定性的、抢占式的,并且包含有界延迟(因此满足了硬实时系统的要求)。然而,这些延迟的最坏情况行为可能慢得令人无法接受。
为了使 Linux 可用于硬实时应用,米兰理工大学航空航天工程系 (DIAPM) 的成员设想了一个实时硬件抽象层 (RTHAL),可以在其上安装实时应用接口 (RTAI)。不幸的是,进一步的调查显示,1996 年末可用的 Linux 内核 2.0.25 尚不够成熟,无法实现该概念。
大约在同一时间,新墨西哥矿业及科技学院 (NMT) 的 Victor Yodaiken 领导的一个小组在 Soccorro, NM 推出了其实时 Linux (RTLinux),这为 DIAPM 团队提供了进一步了解 Linux 内核、硬件以及提供抢占式和确定性调度所需的修改的机会。转折点出现在 1998 年,Linux 2.2.x 内核进行了关键改进,包括对 Linux/硬件接口进行了急需的架构更改。这些更改,加上 DIAPM 团队在使用他们自己演变的 NMT-RTLinux 内核以及 1996 年的概念时获得的经验,最终促成了 RTAI 的诞生。
RTAI 提供有保证的硬实时调度,但保留了标准 Linux 的所有特性和服务。此外,RTAI 还支持 UP 和 SMP——能够将任务和 IRQ 分配给特定的 CPU、x486 和 Pentium、同步单次和周期性调度程序、Linux 间和 Linux 内共享内存、POSIX 兼容性、FPU 支持、任务间同步、信号量、互斥锁、消息队列、RPC、邮箱、从标准用户空间中使用 RTAI 系统调用的能力等等。
RTLinux 和 RTAI 的底层架构非常相似。对于每种实现,Linux 操作系统都作为小型实时操作系统的最低优先级任务运行。因此,从用户或 Linux 内核的角度来看,Linux 的操作没有发生任何变化,只是只有在没有实时任务执行时才允许执行。从功能上讲,两种架构都提供了运行特殊实时任务和中断处理程序的能力,这些任务和中断处理程序在需要时执行,而无需考虑 Linux 可能正在执行的其他任务。两种实现都通过允许实时任务和中断处理程序通过设备接口或共享内存与普通 Linux 进程通信,从而将标准 Linux 编程环境扩展到实时问题。
两种实现之间的主要架构差异在于如何将这些实时功能添加到 Linux 中。RTLinux 和 RTAI 都利用 Linux 的可加载内核模块来实现实时服务。然而,两者之间的关键区别之一是如何将这些更改(添加实时扩展)应用于标准 Linux 内核。
RTLinux 将大多数更改直接应用于内核源文件,从而导致对大量 Linux 内核源文件进行修改和添加。因此,它增加了对 Linux 内核源文件的侵入,这可能会导致代码维护成本增加。它还使跟踪内核更新/更改和查找错误变得更加困难。
RTAI 通过添加硬件抽象层 (HAL) 来限制对标准 Linux 内核的更改,该硬件抽象层 (HAL) 由指向中断向量的指针结构以及中断启用/禁用功能组成。HAL 的实现方式是修改少于 20 行的现有代码,并添加大约 50 行的新代码。这种方法最大限度地减少了对标准 Linux 内核的侵入,并将中断处理和仿真代码本地化,这是一种更加优雅的方法。HAL 技术的另一个优点是,可以通过将 RTHAL 结构中的指针更改回原始指针来将 Linux 恢复为标准操作。事实证明,当实时操作处于非活动状态或尝试隔离晦涩的错误时,这非常有用。
许多人推测,HAL 可能会通过实时任务路径导致不可接受的延迟和延迟。事实上,HAL 对内核性能的影响可以忽略不计,这高度反映了 Linux 内核的成熟度和设计,以及为内核开发做出贡献的人们。
HAL 支持五个核心可加载模块,这些模块提供所需的按需实时功能。这些模块包括 rtai,它提供基本的 rtai 框架;rtai_sched,它提供周期性或单次调度;rtai_mups,它提供同步单次和周期性调度程序或两个周期性调度程序,每个调度程序都有不同的基准时钟;rtai_shm,它允许 Linux 间、实时任务和 Linux 进程之间的内存共享,以及 Linux 内作为 UNIX V IPC 的替代品;以及 rtai_fifos,它是 NMT RTLinux FIFO(先进先出文件)的改编版。
与所有内核模块一样,这些模块可以在需要或释放其各自的功能时加载和卸载(使用标准 Linux insmod 和 rmmod 命令)。例如,如果您只想安装中断处理程序,则只需加载 rtai 模块。如果您还想使用 FIFO 与标准 Linux 进程通信,则需要加载 rtai 和 rtai_fifos 模块。这种模块化和非侵入式架构允许 FIFO 在任何队列上运行,并在必要时使用立即唤醒。
实时任务的实现方式与 RTAI 类似;即,它被编写和编译为内核模块,该模块在加载所需的 RTAI 核心模块后加载到内核中。这种架构产生了一个简单且易于维护的系统,该系统允许动态插入所需的实时功能和任务。下面的示例显示了在实时中调度任务所需的一切
insmod /home/rtai/rtai insmod /home/rtai/modules/rtai_fifo insmod /home/rtai/modules/rtai_sched insmod /path/rt_process
停止您的应用并移除 RTAI
rmmod rt_process rmmod rtai_sched rmmod rtai_fifo rmmod rtai设施 ldmod 和 remod 可用于加载和卸载核心模块。
RTAI 在 Pentium 和 486 类 CPU 上同时支持单次定时器和周期性定时器。虽然同时支持周期性定时器和单次定时器,但它们可能无法同时实例化;即,单次任务和周期性任务可能无法同时作为模块加载到内核中。
使用这些定时器(由 rtai_sched 实例化),根据 CPU、总线速度和芯片组性能,支持超过 90KHz 的周期性速率。在 Pentium 处理器上,支持超过 30KHz 的单次任务速率(Pentium II,233 MHz),在 486 机器上,单次实现提供大约 10KHz 的速率,所有这些都保留了足够的备用 CPU 时间来服务标准 Linux 内核。
rtai_sched 对同时支持单次定时器和周期性定时器的限制可以通过多处理器 (MUP) 实时调度程序 (rtai_mups) 来缓解,该调度程序能够同时使用周期性定时器和单次定时器或两个具有不同周期的周期性定时器,性能与上述 rtai_sched 下的性能相当。请注意,由于 MUP 使用 APIC(高级可编程中断控制器)定时器,因此它无法在 SMP 下运行,并且需要将每个 MUP 调度的任务锁定到特定的 CPU(因此称为多处理器);但是,MUP 保留了所有协调和 IPC 服务,因此不会丢失其他功能。
实时任务/ISR(中断服务例程)中的浮点运算是可能的,前提是在加载时将这些任务标记为需要 FPU 的任务。此方法提供实时任务对 FPU 的访问,同时仍然允许 FPU 访问标准 Linux 任务。
RTAI 通过允许直接与底层 PC 硬件交互(如果选择的话),而无需首先通过标准 Linux 内核的中断管理层,从而提供对硬件的有效且即时的访问。
如下文更详细描述的那样,将特定 IRQ 单独分配给特定 CPU 的能力允许硬件的即时、响应式和有保证的接口时间。
术语进程间通信 (IPC) 描述了活动进程或任务之间消息传递的不同方式,并且还描述了数据传输的多种同步形式。
Linux 提供标准 System V IPC,形式为共享内存、FIFO、信号量、互斥锁、条件变量和管道,标准用户进程可以使用它们来传输和共享数据。虽然这些 Linux IPC 机制不适用于实时任务,但 RTAI 提供了另一组 IPC 机制,其中包括共享内存、消息队列、实时 FIFO、互斥锁、信号量和条件变量。这些用于在实时域和 Linux 用户空间域中的任务和进程之间传输和共享数据。
RTAI 的远程过程调用 (RPC) 机制在操作上类似于实时任务可用的 QNX 消息,并传输无符号整数或指向目标任务的指针。
RTAI 邮箱实现提供了从用户空间向实时任务、从实时任务向实时任务以及从用户任务向用户任务(使用 LXRT)发送任何消息的能力,通过任何可定义邮箱缓冲区大小的方式。允许多个发送者和接收者,其中每个发送者和接收者都根据其优先级进行服务。
从 0.9 版本开始,RTAI 包括一个 proc 接口,该接口提供有关 RTAI 当前状态的有用信息,包括加载的调度程序;实时任务活动、优先级和周期;以及正在使用的 FIFO 及其关联的缓冲区大小。更多功能的开发目前正在进行中。
RTAI 通过其任务和 IRQ 管理为对称多处理 (SMP) 架构提供真正的支持。
默认情况下,所有任务都分配为在任何 CPU(SMP 平台)上运行。但是,每个任务都可以单独分配给任何 CPU 子集,甚至单个 CPU。此外,可以将任何实时中断服务分配给任何特定的 CPU。由于强制中断到特定 CPU 的能力与 SMP 调度程序无关,因此 RTAI 保留了独立执行这两个操作的灵活性。
如果手动任务分配比 Linux 的自动 SMP 负载分配服务更有效地处理任务,则这些功能提供了一种静态优化实时应用的方法。
由于实时 Linux 任务是作为可加载模块实现的,因此在所有实际用途中,它们都是内核的组成部分。因此,这些任务不受 Linux 的内存保护服务的约束,并且它们能够覆盖内存的系统关键区域,从而导致系统过早停止。对于我们这些在实时任务开发过程中犯过错误的人来说,这种限制一直令人沮丧。
RTAI 的 LXRT 通过允许使用来自 Linux 内存保护空间内的所有 RTAI 硬实时系统调用以及在“软”实时服务下开发实时任务来解决此问题。当开发人员对 LXRT 中的任务性能感到满意时,只需将任务重新编译为模块并插入内核(以及提供 RTAI 实时功能的关联模块),即可从软实时过渡到硬实时。
LXRT 的软实时服务类似于堪萨斯大学实时 (KURT) 补丁提供的服务,它提供软实时与细粒度任务调度的结合。LXRT 下的性能非常好,产生的延迟不会比导致任务切换的标准 Linux 系统调用大多少。虽然这作为一种开发工具非常有价值,但我们不应忽视这样一个事实,即 RTAI 的软实时实现可以证明对于那些不需要硬实时,但又不太满意标准 Linux 的调度性能的任务特别有用。
RTAI 通过使用单个可加载模块来实现 POSIX 1003.1.c 的兼容子集。这些调用支持线程、互斥锁和条件变量的创建、删除、属性控制和环境控制。由此产生的 POSIX 支持类似于标准 Linux 线程,只是不支持父子函数(这不适用于实时任务,因为所有线程都被视为单个进程的一部分)和信号处理(目前正在开发中)。
从成本和性能的角度来看,RTAI 现在与当前可用的商业 RTOS 具有竞争力。
由于任何 RTOS 系统的性能都由 RTOS 本身的性能、其运行所在的硬件性能以及用于获取数据的测试程序决定,因此绝对性能数据很难量化,通常使得在基本相似的 RTOS 之间进行比较变得困难。但是,以下数据是在典型的 Pentium II 233MHz 和 486 平台上测量的,并且具有代表性。
对于这些性能表征,早期版本的 RTHAL 模块被演示以 125KHz(Pentium II,233MHz)运行定时器,同时为在重负载下工作的 Linux 提供服务。在此演示期间,关于周期性定时器的平均和最大抖动分别为 0µs 和 13µs。此性能与附加测试相结合,可以总结为以下方式
最大周期性任务迭代速率:125KHz
典型采样任务速率:10KHz (Pentium 100)
最大任务迭代速率下的抖动:0-13µs UP,0-30µs SMP
单次中断集成速率:30KHz(奔腾级 CPU),10KHz(486 级 CPU)
上下文切换时间:约 4µs
通过 DIAPM 开发团队和互联网上那些受到 RTAI 灵活架构、高性能和功能集鼓舞的人们的共同贡献,RTAI 继续发展和成熟。虽然这种实时 Linux 扩展在今天非常强大、稳定和成熟,但工作远未完成。未来有很多事情要做,包括
VxWorks 兼容性库
pSOS 兼容性库
增强的开发工具
增强的调试工具
实时以太网功能
此处提供的信息是根据 RTAI 和 RealTime Linux 的当前性能和功能集制作的。但是,RTAI 和 Zentropix 主页始终包含最新的信息。
P. Mantegazza 在米兰理工大学航空航天工程系工作。
E. Bianchi 在米兰理工大学航空航天工程系工作。
L. Dozioi 在米兰理工大学航空航天工程系工作,可以通过 luca.bianchi@infementia.it 联系到他。
S. Papacharalambous 在 Zentropix Computing, LLC. 工作。(注意:Zentropix 于 2000 年 2 月 1 日被 Lineo 收购。)
S. Hughes 在 Zentropix Computing, LLC. 工作。(注意:Zentropix 于 2000 年 2 月 1 日被 Lineo 收购。)
D. Beal 在 Zentropix Computing, LLC. 工作,可以通过 daveb@zentropix.com 联系到他。(注意:Zentropix 于 2000 年 2 月 1 日被 Lineo 收购。)