第三章:软件冗余技术——N版本编程、恢复块与表决算法
软件冗余,说白了就是「多写几份代码,让它们互相盯着」。
我在刚入行那会儿,总觉得硬件冗余才叫真功夫,软件嘛,改改bug就行了。直到有一次,一个PLC的固件在特定工况下死循环,整个产线停了4小时……嗯,从那以后,我对软件冗余再也不敢马虎。
3.1 N版本编程(N-Version Programming)
这个思路很直接:同一个需求,让三个不同的团队,用三种不同的语言或工具去实现。然后同时运行,结果投票。
你想想看,三个团队都犯同一个错的概率,是不是极低?
3.1.1 典型架构
- 版本A: C语言 + RTOS,侧重性能
- 版本B: 梯形图 + 专用PLC,侧重可靠性
- 版本C: Python原型 + 形式化验证,侧重逻辑正确性
三个版本同时接收输入,各自计算,输出送给表决器。
3.1.2 我踩过的坑
我曾经在一个核电项目中用过N版本编程。三个团队各自开发,结果联调时发现——版本A和版本B对「超限报警阈值」的理解差了0.5%。
为什么?因为需求文档里写的是「接近上限时报警」,一个团队理解为95%,另一个理解为99%。
教训: N版本编程的前提是——需求规格必须绝对精确,不能有任何歧义。
3.1.3 代码示例(伪代码)
// 表决器核心逻辑
float output = vote(
versionA(input),
versionB(input),
versionC(input)
);
// 投票函数(三取二)
float vote(float a, float b, float c) {
if (abs(a-b) < THRESHOLD) return (a+b)/2;
if (abs(a-c) < THRESHOLD) return (a+c)/2;
if (abs(b-c) < THRESHOLD) return (b+c)/2;
return ERROR; // 三个都不一致,报错
}
3.2 恢复块技术(Recovery Blocks)
这个技术跟N版本编程不一样。它不是同时跑多个版本,而是「主版本先跑,出错了再换备胎」。
说白了,就像你开车——主刹车失灵了,还有手刹。手刹再不行,还有电子驻车。
3.2.1 工作流程
- 主块(Primary Block) 先执行
- 执行完后做 验收测试(Acceptance Test)
- 如果测试通过,输出结果
- 如果测试不通过,回滚到上一个检查点,执行 备选块(Alternate Block)
- 备选块再失败,继续下一个备选……直到所有备选都用完
3.2.2 避坑指南
我曾经在一个飞行控制系统中用恢复块技术。主块是C语言写的,备选块是Ada写的。结果发现——主块崩溃时,内存已经被污染了,备选块读到的数据全是错的。
解决方案: 每个备选块启动前,必须从干净的检查点恢复数据。不能共享内存空间。
3.2.3 伪代码示例
// 恢复块结构
bool recovery_block(Input in, Output *out) {
// 保存检查点
save_checkpoint();
// 尝试主块
if (primary_block(in, out) && acceptance_test(out)) {
return true;
}
// 主块失败,恢复检查点
restore_checkpoint();
// 尝试备选块1
if (alternate_block_1(in, out) && acceptance_test(out)) {
return true;
}
// 备选块1也失败
restore_checkpoint();
// 尝试备选块2(最保守的版本)
if (alternate_block_2(in, out) && acceptance_test(out)) {
return true;
}
// 全部失败
return false;
}
3.3 一致性检查与表决算法
不管是N版本编程还是恢复块技术,最后都绕不开一个问题——怎么判断多个结果「一致」?
这就是一致性检查和表决算法的活儿了。
3.3.1 常见表决算法
| 算法名称 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 多数表决(Majority Vote) | 离散值(如开关量) | 简单、快速 | 奇数个版本才有效 |
| 中值表决(Median Vote) | 连续值(如温度、压力) | 抗单个野点 | 计算稍慢 |
| 加权表决(Weighted Vote) | 各版本可靠性不同 | 更精准 | 需要先验可靠性数据 |
| 一致性表决(Consensus Vote) | 高安全等级 | 严格、无歧义 | 可能无结果 |
3.3.2 一致性检查的要点
- 时间一致性: 所有版本必须在同一时刻采样输入。我见过一个项目,版本A采样早了10ms,版本B晚了5ms,结果两个值永远对不上。
- 数值一致性: 允许一定的误差范围(比如±1%),而不是要求完全相等。
- 顺序一致性: 如果输出是一个序列(比如轨迹点),必须保证顺序也一致。
3.4 知识体系图
3.5 实战建议
我个人习惯,在项目初期就确定好冗余策略:
- 安全等级SIL3及以上: N版本编程 + 恢复块 + 三取二表决,三层防护
- SIL2: N版本编程 + 中值表决,两层就够了
- SIL1: 恢复块技术 + 简单的验收测试
记住一点:软件冗余不是越多越好。版本太多,维护成本会爆炸。我见过一个项目搞了5个版本,结果每次需求变更,5个团队都要改,改完还要重新做一致性测试……最后项目延期了半年。
好了,软件冗余的核心就这些。下一节我们聊聊硬件冗余——那又是另一番天地了。
公众号:蓝海资料掘金营,微信deep3321