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

Ada Exceptions in Safety Critical Embedded Systems

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

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

尽职调查:我很难确定 Ada 异常的确定性,但我的理解是它们的原始实现需要更多 运行 时间开销以换取减少代码大小的影响(上面第一个 link 明确提到了 Ada)。除了上述第一个 link,我还研究了提到代码确定性的配置文件,如 Ravenscar 配置文件和 this paper,但似乎没有提到异常处理确定性。说句公道话,我可能看错地方了,因为这个话题似乎很深。

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

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

我自己没有在安全关键系统上工作过,但我确信阿丽亚娜 4 惯性导航系统中软件使用的 运行 时间包括例外情况。

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

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

硬实时的嵌入式系统可能有约束也可能没有。同事们在 70 年代研究了一个导弹制导系统,该系统在其主循环中有大约 4 个指令的余量! (您可以想象,它是用汇编程序编写的,并使用了经过调优的执行程序,而不是 RTOS。不支持异常)。另一方面,我在 1 GHz PowerPC 板上工作的最后一个响应特定中断的截止日期为 2 毫秒,我们测量的最坏情况是 1.3 毫秒(无论如何这是一个软实时要求,您不必连续错过太多)。

该系统也有安全要求(我知道,我知道,安全导弹系统,呵呵)虽然我们被允许使用异常,但未处理的异常意味着系统必须关闭,导弹在飞行中或不,导致导弹丢失。而且我们被严格禁止说 when others => null; 吞下一个异常,所以任何我们没有处理的异常都会 'unhandled' 并且会跳到顶层。

论点是,如果发生未处理的异常,您将无法再了解系统的状态,因此您无法继续操作。当然,更广泛的安全工程必须考虑整个系统应该采取什么行动(例如,也许这个处理器应该以恢复模式重启)。

有时人们将异常用作控制流程的一部分;事实上,对于处理随机文本输入,一种常用的方法是,而不是检查文件结尾,只是继续直到你得到 End_Error;

loop
   begin
      --  read input
      --  process input
   exception
      when End_Error => exit;
   end;
end loop;

discusses using SPARK. You don't have to use SPARK to not handle exceptions, though of course it would be nice to be able to prove to yourself (and your safety auditor!) that there won't be any. Handling exceptions is very tricky, and some RTSs (e.g Cortex GNAT RTS) 不;配置编译指示

pragma Restrictions (No_Exception_Propagation);

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

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

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

将是避免 "do more" 代码的一种相当混乱的方式。最好使用标签!