安全关键嵌入式系统中的Ada异常

3
我开始学习Ada,是因为它有潜在的用于安全关键嵌入式设备的能力。到目前为止,我非常喜欢它。然而,在我对嵌入式编程的研究中,我遇到了一个热门话题,即是否应该在嵌入式系统中使用异常处理。我认为我理解了一些人似乎避免使用它的原因: 现在我的问题是,Ada语言或GNAT编译器是否解决了这些问题? 我对安全关键代码的理解是,非确定性的代码大小和执行时间通常是不可接受的。
尽职调查:我有些困难找到精确了解 Ada 异常的确定性的方法,但我的理解是它们的原始实现要求更多的运行时开销以换取减少代码大小的影响(上面的第一个链接明确提到了 Ada)。除了上面的第一个链接,我还研究了提及代码确定性的配置文件,比如 Ravenscar 配置文件和this paper,但似乎没有提到异常处理的确定性。公平地说,我可能在错误的地方寻找,因为这个主题似乎非常深入。

@LoneWanderer,是的,我指的是内置的异常和抛出系统。实现自己的方式也是一个合理的观点。虽然我不排斥这个选项,但我只是想确保我没有不必要地重复造轮子。我对Ada还比较新,但通过研究C中全面错误处理的策略,我意识到这是多么具有挑战性。如果有比我更有经验的人(即提出Ada标准的委员会)已经做出了一个很好的解决方案,我希望至少知道一下。 :) - silentTeee
4
这不是一项调查,而是关于Ada编译器如何实现语言中特定功能的问题。还有关于如何解决该功能的次优实现的方法。 - Jacob Sparre Andersen
1
@JacobSparreAndersen:_耸肩_在这种情况下,你可以选择关闭问题,理由是范围过大。实际上,两个都是有效的关闭原因。 - too honest for this site
这个问题基本上是一个“调查”类型的问题,它询问在一类广泛的编译器中如何具体实现,例如“Ada或其编译器 - 在军事应用等安全关键场景中经常使用的工具”。这类问题往往会有多个答案,基本上都陈述了用户在他们有经验的子集中的经验。这导致不可能确定一个答案,获得覆盖整个集合的答案,甚至判断一个答案是否真正正确。这样的问题通常被认为是过于广泛/基于观点而不适合讨论。 - Makyen
1
有可能你可以重新提出一个更加具体的问题,直击你感兴趣的核心问题,或者专注于某个方面,或者只涉及一个编译器。 - Makyen
显示剩余5条评论
2个回答

12
有嵌入式系统具有安全或任务关键性,有硬实时的嵌入式系统,也有同时具备以上两种特性的嵌入式系统。
硬实时的嵌入式系统可能受到限制,也可能不受约束。同事们在70年代曾经开发出一款导弹制导系统,其主循环只有大约4个指令的空间!(可以想象,它是用汇编语言编写的,并使用了调谐执行程序,而不是RTOS。不支持异常处理)。另一方面,我工作过的最后一个系统,在1 GHz的PowerPC板上,对于响应特定中断的反应时间有2毫秒的截止期限,我们测量得到的最坏情况为1.3毫秒(而且这只是软实时要求,你只需要不连续错过太多次)。
那个系统还有安全要求(我知道,我知道,安全导弹系统啊),尽管我们被允许使用异常,但未处理的异常意味着系统必须关闭,无论导弹是否飞行,导致导弹失效。我们严格禁止使用 when others => null; 来吞掉异常,因此我们没有处理的任何异常都将被视为“未处理”,并将弹回到顶层。
论据是,如果发生未处理的异常,你将无法再知道系统的状态,因此不能继续进行。当然,更广泛的安全工程必须考虑整个系统应该采取什么行动(例如,也许这个处理器应该在恢复模式下重新启动)。
有时人们将异常用作其控制流的一部分;事实上,对于处理随机文本输入,经常使用的方法是,而不是检查文件结束,只要继续直到获得 End_Error 为止。
loop
   begin
      --  read input
      --  process input
   exception
      when End_Error => exit;
   end;
end loop;

Jacob的回答讨论了使用SPARK。你不必使用SPARK来不处理异常,尽管当然可以证明给自己(和您的安全审计员!)不存在任何异常会更好。 处理异常非常棘手,一些运行时系统(例如Cortex GNAT RTS)不会处理; 配置指示

pragma Restrictions (No_Exception_Propagation);

意味着异常无法传播到它们引发的范围之外(程序将通过调用Last_Chance_Handler而崩溃)。

仅在引发异常的范围内传播异常并不是很有用,我认为:

begin
   --  do something
   if some error condition then
      raise Err;
   end if;
   --  do more
exception
   when Err =>
      null;
end;
使用"do more"代码的方式可能会导致混淆,更好的方法是使用标签!

1
有时候人们会将异常作为控制流的一部分。是的,https://twitter.com/flibitijibibo/status/1037011978600898560 - Peter Mortensen

9
在Ada中,异常是确定性的。(但是一些可能会引发异常的检查有一定的自由度。如果编译器能够提供正确的答案,当一个中间结果超出了所涉及类型的范围时,它并不总是必须引发异常。)
至少有一个Ada编译器(GNAT)具有“零成本”异常实现。这并不意味着异常完全免费,但直到实际引发异常之前,您不需要支付运行时成本。您仍然需要付出代码空间方面的代价。该成本的大小取决于架构。
我自己没有在安全关键系统上工作过,但我确信用于Ariane 4惯性导航系统软件的运行时包括异常。
如果您不想使用异常,一个选择是使用SPARK(从Ada派生的语言)。您仍然可以使用任何Ada编译器,但您需要使用SPARK工具来证明程序不能引发任何异常。您应该注意,SPARK并不是魔法。您需要通过插入断言来帮助工具,工具可以将其用作证明的中间步骤。

我找到了描述GNAT的“零成本”异常实现的文档,但我想问一下你是从哪里得知异常是确定性的呢?还是这只是由于实现的“零成本”特性而导致的?我是那种热衷于学习的人,喜欢在空闲时间阅读这些技术细节...而且我可能会试图向我的管理层证明一个使用案例。:P - silentTeee
标准(http://rm.ada.wtf/12/RM-TOC.html)非常清楚地说明了异常的工作方式。对于非确定性传播或处理异常没有任何空间。 - Jacob Sparre Andersen
在解开异常时,传递任何范围都会涉及到该范围中声明的实体的最终化。由于最终化可能涉及到资源的释放和回收等操作,所以我认为它不能被称为确定性的! - Simon Wright
@SimonWright 如果这是你对非确定性的定义,那么即使离开一个declare块也是一种非确定性操作。我们在这个问题上似乎有不同意见。 - Jacob Sparre Andersen
我认为我使用的词是根据我理解 OP 的使用方式 - 任何可能存在多个异常并且没有定义哪个异常将在某种情况下处理的语言都会很奇怪。我想你可能会在最终化期间引发 PE,这是由于其他异常引起的;我猜测 PE 会被传播...? - Simon Wright

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接