2

我开始学习 Ada 是因为它在安全关键的嵌入式设备中的潜在用途。到目前为止,我真的很喜欢它。然而,在我对嵌入式编程的研究中,我遇到了一个热门话题,即是否在嵌入式系统中使用异常处理。我想我理解为什么有些人似乎避免它:

现在我的问题是,Ada 语言或 GNAT 编译器是否解决了这些问题?我对安全关键代码的理解是,不确定的代码大小和执行时间通常是不可接受的。

尽职调查:我在确定 Ada 异常的确切程度时遇到了一些麻烦,但我的理解是它们的原始实现需要更多的运行时开销以换取减少代码大小的影响(以上第一个链接明确提到了 Ada)。除了上面的第一个链接之外,我还查看了提到代码确定性的配置文件,例如 Ravenscar 配置文件和这篇论文,但似乎没有提到异常处理确定性。公平地说,我可能找错地方了,因为这个话题似乎很深。

4

2 回答 2

10

有安全或任务关键型的嵌入式系统,硬实时的嵌入式系统,以及两者兼而有之的嵌入式系统。

硬实时的嵌入式系统可能会受到约束,也可能不受约束。同事们在 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;

雅各布的回答讨论了使用 SPARK。您不必使用 SPARK 来不处理异常,尽管能够向您自己(和您的安全审计员!)证明不会有任何异常当然会很好。处理异常非常棘手,一些 RTS(例如Cortex GNAT RTS)不这样做;配置编译指示

pragma Restrictions (No_Exception_Propagation);

意味着异常不能传播到引发它们的范围之外(程序将因调用 a 而崩溃Last_Chance_Handler)。

仅在引发异常的范围内传播异常并不是,IMO,有用:

begin
   --  do something
   if some error condition then
      raise Err;
   end if;
   --  do more
exception
   when Err =>
      null;
end;

避免“做更多”代码是一种相当混乱的方式。最好使用标签!

于 2018-09-02T18:31:01.097 回答
8

异常在 Ada 中是确定性的。(但是一些可以引发异常的检查有一定的自由度。如果编译器可以提供正确的答案,它并不总是必须引发异常,如果中间结果超出了所讨论的类型的范围。)

至少一个 Ada 编译器 (GNAT) 具有“零成本”异常实现。这不会使异常完全免费,但在您实际引发异常之前,您无需支付运行时成本。您仍然需要在代码空间方面付出代价。成本有多大取决于架构。

我自己没有研究过安全关键系统,但我确信 Ariane 4 惯性导航系统中用于软件的运行时包含异常。

如果您不想要异常,一种选择是使用 SPARK(一种源自 Ada 的语言)。您仍然可以使用任何您喜欢的 Ada 编译器,但是您使用 SPARK 工具来证明程序不会引发任何异常。你应该注意到 SPARK 不是魔法。您必须通过插入断言来帮助工具,工具可以将其用作证明的中间步骤。

于 2018-09-02T17:16:10.600 回答