实时操作系统(RTOS)不仅仅是一种应用程序接口,就像一些库一样。然而,倡导RTOS抽象的人似乎认为RTOS API才是最重要的。我第一次听到“所有RTOS都一样”的误解是在20年前。从那时起,似乎越来越多的人在犯同样的错误,并将开发人员引入歧途。
RTOS抽象基本上是其所涵盖的RTOS之间的最小公分母。因此,它只能是一个轻量级的RTOS API,失去了底层RTOS的许多强大功能。项目和公司不应该像蝴蝶在花丛中飞舞一样,从一个RTOS跳到另一个RTOS。虽然这在理论上很有吸引力,但并不是一个好的规划。
谨慎选择RTOS的原因
项目和公司应选择一个功能强大的RTOS并坚持使用。有些RTOS实现得很好,而有些则实现得很差。如果在RTOS中缺少某个需要的功能,那就需要在应用程序中添加绿色代码,这会在重新设计、实现和测试方面浪费许多精力。为什么有人想要这样做呢?
例如,有些RTOS提供非递归的互斥体,这在实时嵌入式系统中是最糟糕的。如果一个任务需要测试同一个互斥体两次,它就会停止运行。提供递归互斥体所需的额外RTOS代码量是微不足道的。有些RTOS会为所有可能导致任务等待的操作提供超时机制,但有些RTOS则没有。在前面的例子中,如果没有超时机制,任务将永远停止,最终可能导致系统瘫痪,而如果有超时机制,这一任务将恢复运行并及时报告问题。
RTOS的一个重要目标是帮助开发人员避免错误。例如,有些实时操作系统会检查服务调用中的每个参数,有些RTOS则很少或根本不检查。参数检查可以减少调试时间,而且对于已交付的系统来说,它还有助于防止潜在的缺陷、黑客攻击和软错误。
Ralph Moore,RTOS创新师,Micro Digital公司
我研究过的许多RTOS都存在有问题的行为。例如,有某个著名的RTOS,在一个任务优先级发生变化时,无法将该任务重新放入队列,这与人们的逻辑预期不符。事实上,提高任务的优先级,很可能只是为了在等待某个事件时,让任务能被更快地执行。在这种情况下,程序员可能会感到困惑,为什么提高某任务的优先级后,该任务的性能却并没有提高。再比如,如果队列被删除,同样,这个RTOS无法自动释放队列中等待的任务。相反,却建议开发人员:“如果队列中有任务在等待,就不要删除这个队列”。这是无助于开发人员避免错误的一个很好例子,试想,谁会去看那些繁琐的细则呢?
另一个著名的RTOS在给一个块指定指针后,并不指定向哪个堆释放该块。这是一个微妙的问题:如果指针是错的,会发生什么?一种更严谨的实现方式要求指定堆,以便在使用指针前对指针进行范围测试。这是另一个防止程序员自己犯错的例子。上述函数也不能防止双重释放(double-free)错误,这也是一个常见的编程错误。同样,这个RTOS还实现了消息队列刷新服务,并向等待将其消息放入队列的任务返回成功标志。这似乎也有违直觉。难道不应该通知这些任务,让其知道它们的消息没有送达,以便它们采取纠正措施吗?
另一个重点是如何处理中断服务例程(ISR)。大多数RTOS允许从ISR直接调用某些内核服务或修改后的内核服务。这有悖于良好的编程实践,即尽可能缩短ISR,即使在重负载情况下,也能确保及时完成ISR。如果不这样做,就有可能导致错过ISR,并导致系统产生错误行为。
此外,由于允许ISR调用内核服务,黑客的攻击面将大大增加。虽然一个小的ISR可能没有漏洞,但内核服务不可能没有漏洞。此外,延迟中断处理是由任务执行的,很可能是从ISR发出信号。这样做的问题是,该任务可能会被其他任务阻塞,从而过迟完成中断处理。更好的方法是ISR调用链接服务例程(LSR),而LSR在所有ISR之后、所有任务之前运行。这不仅可以提供可靠的性能,还能将ISR的大部分代码卸载到LSR中。
许多RTOS允许任务控制块(TCB)和其他控制块的大小野蛮式增长。但控制块必须尽可能紧凑,否则,可能需要限制任务或其他RTOS对象的数量,从而导致性能达不到最优。
安全性
认为安全系统可以建立在轻量级内核抽象之上,是一厢情愿的想法。我们的高安全性实时操作系统SecureSMX,几乎使用了功能丰富的smx内核中所有功能。一个高度安全的系统至少需要以下这些内核功能:
●全面检查服务调用参数,并向中央错误管理器报告错误,以便采取适当的措施(如分区重启,并向安全中心报告)。
●在事件缓冲区中记录所有操作或其中选定的子集,并定期上传到安全中心,以检查是否有可能存在非法闯入或潜在缺陷的异常活动。
●支持多个堆。这样,需要堆服务的隔离分区就可以拥有自己的本地堆。(分区之间共享堆为黑客在共享堆的分区之间提供了一条很好的入侵路径)。
●将Cortex v7M和v8M区域的堆分配用于受保护信息、受保护块和任务堆栈。
●需要一个用于多入口站点的消息交换系统,该系统需能够支持:
➤可变大小的消息
➤消息优先级(使优先级较高的消息得到优先服务)
➤将消息优先级传递给服务器任务(控制其运行时间)
➤回复交换规范(用于结果返回)
●保护自带区域信息的消息。这些消息被加载进MPU和服务器的MPA中,以便于服务器处理报文。这些是能够在分区之间提供强隔离端口的基础。
●有一个有效的运行时间限制系统,而不仅仅是时间分片。
●具有递归和优先级继承或上限优先级互斥,或两者兼而有之。
●从公共池中共享堆栈的一次性任务。这些任务非常有用,因为分区总是会导致增加更多任务,其中大部分任务只是完成一个简单功能(如处理来自端口的消息),然后等待下一个请求,不需要堆栈。
●有一种可最大限度地减少ISR代码,并在隔离分区内进行延迟中断处理的有效方法。
应该指出的是,一个能够帮助程序员避免错误的内核,也有助于减少漏洞。遗憾的是,无论是静态代码分析器,还是其他代码分析工具,都无法检测出RTOS使用漏洞。
将上述所有功能添加到轻量级内核抽象中,将产生大量的绿色代码,而这些代码需要重新设计和测试,耗时将达数年!
本文小结
认为一些轻量级内核抽象可以解决当今对安全可靠系统需求的想法是严重错误的。项目需要在RTOS API下检查驱动其应用程序的引擎,以确定该引擎的功能是否足够强大,否则将导致更多的预算超支甚至项目取消,甚至更糟的诉讼。安全问题也绝不能掉以轻心。
备注:本文中“内核”和“RTOS”这两个术语可以互换使用。
(参考原文:rtos-abstractions-are-wrong)
本文为《电子工程专辑》2023年12月刊杂志文章,版权所有,禁止转载。点击申请免费杂志订阅