为高放射性环境编译应用程序

为高放射性环境编译应用程序

技术背景

在高放射性环境中,如微型卫星等,电子组件容易受到单粒子翻转(SEU)的影响,导致软件和固件出现错误。为了确保应用程序在这种环境下的可靠性,需要从软件层面采取一系列措施来检测和恢复错误。

实现步骤

1. 提供实时更新/重新编译/刷新功能

在高电离环境中,软件和固件应具备实时更新、重新编译和刷新的能力,以应对可能出现的错误。

2. 准备最小工作版本的副本

在代码中准备多个响应式的最小版本软件/固件副本,类似于 Windows 的安全模式。这些最小版本通常只具备监听外部系统命令、更新当前软件/固件以及监控基本操作的内务数据等功能。

3. 准备冗余软件/固件

- 在 ARM uC 中准备冗余软件/固件,可将两个或多个相同的软件/固件放置在不同地址,并相互发送心跳信号,一次仅激活一个。若某个软件/固件无响应,则切换到其他副本。
- 在外部系统(如卫星的任务控制中心)中至少保留一个副本,以便与设备通信并更新软件/固件。
- 在设备的永久内存存储中保留副本,可触发其恢复运行系统的软件/固件。

4. 检测错误情况

错误必须能够被检测到,通常通过硬件的错误纠正/检测电路或一小段用于错误纠正/检测的代码实现。建议将此类代码编写得小而独立于主软件/固件,主要任务仅为检查/纠正。对于硬件电路/固件可靠的情况,可考虑进行错误纠正;否则,仅进行错误检测,由外部系统/设备进行纠正。

5. 确保硬件支持恢复

恢复操作最终依赖于负责恢复的硬件正常工作。若硬件永久损坏(通常在达到总电离剂量阈值后),软件将无法进行恢复。因此,对于高辐射环境下的设备,硬件的可靠性至关重要。

核心代码

安全激活代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
... code that checks system state
if (system_state_favors_activation)
{
prepare_for_activation();
... code that checks system state again
if (system_state_is_valid)
{
if (system_state_favors_activation)
trigger_activation();
}
else
perform_safety_shutdown_and_restart();
}
cancel_preparations();

自定义安全数据类型示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>

template <typename T, size_t N = 3>
class SAFE_T {
private:
T values[N];
public:
SAFE_T(T initialValue) {
for (size_t i = 0; i < N; ++i) {
values[i] = initialValue;
}
}

SAFE_T& operator=(const T& other) {
for (size_t i = 0; i < N; ++i) {
values[i] = other;
}
return *this;
}

T getValue() const {
size_t counts[N] = {0};
for (size_t i = 0; i < N; ++i) {
for (size_t j = 0; j < N; ++j) {
if (values[i] == values[j]) {
++counts[i];
}
}
}
size_t maxIndex = 0;
for (size_t i = 1; i < N; ++i) {
if (counts[i] > counts[maxIndex]) {
maxIndex = i;
}
}
return values[maxIndex];
}
};

int main() {
SAFE_T<int> safeInt(42);
std::cout << safeInt.getValue() << std::endl;
return 0;
}

最佳实践

1. 采用冗余硬件

使用 2 个或更多相同的硬件设置和代码。若结果不同,则触发重置。使用 3 个或更多设备时,可采用“投票”系统识别出现问题的设备。

2. 优化编译器设置

避免使用复杂的编译器设置,以免引入更多未测试的代码路径。可使用一些编译器选项来增强代码的安全性,如 -mmitigate-rop-fstack-protector-all-D_FORTIFY_SOURCE=2-Wl,-z,relro,-z,now-Wl,-z,noexecstack 等。

3. 测试和验证

使用自动化单元测试,在开发系统和目标系统上运行相同的测试用例,查找差异。检查嵌入式设备的勘误表,确保代码没有因硬件问题而崩溃。

4. 采用循环调度器

使用循环调度器,定期检查关键数据的正确性。对于周期性软件,可在周期之间重新初始化堆栈,避免使用中断调用的堆栈。

5. 应用 CRC 校验

对所有内容(包括执行代码)应用循环冗余校验(CRC),定期检查 CRC 值,促使内存的 ECC 控制器修复单比特错误。

6. 使用看门狗

在启动和运行过程中使用硬件看门狗,确保系统在出现问题时能够重启。

常见问题

1. 如何处理多个比特错误?

大多数 ECC 内存只能恢复单比特错误,因此应确保内存扫描频率足够高,以减少多比特错误的发生。

2. 如何确保代码的可靠性?

避免依赖编译器的默认优化设置,禁用可能导致问题的 C++ 特性(如异常、模板、iostream 等)。使用汇编语言编写关键代码,确保对寄存器和内存的精确控制。

3. 如何应对硬件故障?

若硬件故障无法避免,可使用冗余硬件和数据备份,确保在硬件故障时能够快速恢复。同时,通过日志记录和远程调试,定位和解决硬件问题。

4. 如何平衡性能和容错性?

采用冗余和错误纠正机制会增加处理时间和资源消耗,因此需要在性能和容错性之间进行权衡。可根据具体应用场景和需求,选择合适的容错技术。


为高放射性环境编译应用程序
https://119291.xyz/posts/compiling-application-for-highly-radioactive-environments/
作者
ww
发布于
2025年5月29日
许可协议