如何创建关键任务软件?


15

我正在自我学习形式方法。我听说使用(通常仅使用)正式方法来创建任务关键型软件(例如核反应堆控制器,飞机飞行控制器,空间探测器控制器)。这就是为什么我有兴趣学习它的原因:p

但是,在学习了形式化方法(尤其是LTL,CTL及其同级方法)之后,我觉得它们只能用于验证规范的正确性(安全性,活跃性,公平性等)。

但是,然后如何验证软件(不仅是规范)确实正确?

免责声明:在理论计算机科学方面,我是90%的白痴。因此,请在回答时保持仁慈。


2
您对“ ...软件确实正确...”的确切含义什么?您是指以下2项中的哪一项:1)软件符合规范2)特定的代码块尊重某些给定的属性或某些输入-输出关系。
Giorgio Camerani 2012年

@GiorgioCamerani:第一个
fajrian

2
程序的正确性通常意味着(1)它符合规范,并且(2)它绝不会崩溃。要点(1)实际上是关于对(程序,规范)的陈述,而不是关于程序本身的陈述。更复杂的是,“程序”通常是“程序模型”的简写,因为程序本身过于复杂或没有精确的语义。鉴于此,我您是在问程序与其模型之间的差距,但是我不确定。
Radu GRIGore 2012年

@RaduGRIGore:实际上我不明白什么是“模型”。但我认为您已经非常仔细地回答了我的问题。基本上,我想知道的是规范和程序源代码之间的差距。当程序员(如我)执行规范时,可能会发生许多愚蠢的事情。
fajrian 2012年

1
@fajrian:我怀疑您对我称之为“模型”的说法是“规范”。有一些工具可以处理用C或Java等语言或机器代码编写的程序。(不过,这仍然是一个模型,因为它们必须假设一些语义,这些语义应该但不一定与编译器/处理器的行为相对应。)
Radu GRIGore 2012年

Answers:


11

这个问题相当广泛。为了在一个合理的空间内回答这个问题,我将作很多简化。

让我们就术语达成一致。当一个程序暗示其规范时是正确的。通过确定确切是什么程序和确切是什么规范,可以用许多方式使这个模糊的语句精确。例如,在模型检查中,程序是Kripke结构,而规范通常是LTL公式。或者,该程序可以是PowerPC指令的列表,而规范可以是用一阶逻辑编写的一组Hoare-Floyd断言。有很多可能的变化。很容易得出结论,在一种情况下(Kripke结构)我们不验证实际程序,而在第二种情况下(PowerPC指令列表)我们进行验证。但是,重要的是要意识到我们确实在两种情况下都在研究数学模型,这很好。(这种情况与物理学非常相似,例如,经典力学是现实的数学模型。)

大多数形式化区分程序的语法和语义。也就是说,它的表示方式及其含义。从程序验证的角度来看,程序的语义至关重要。但是,当然,重要的是要有一种明确的方法来为程序的(语法表示)分配含义。以下是两种流行的方式:

  • (小步骤)操作语义:这非常类似于通过为其编写解释器来定义编程语言。为此,您需要说什么是state,它受语言中的每个语句的影响。(您可能想知道您使用哪种语言编写口译员,但我假装您不是。)
  • 公理语义:这里每种语句类型都带有一个公理模式。因此,大致而言,只要使用该类型的特定语句,就可以使用某些公理。例如,赋值带有模式{ P [ x / e ] }x:=e ; 特定的 x = x + 1带有公理 { x + 1 = 1 }{P[x/e]}x:=e{P}x:=x+1如果我们用 P = x = 1 实例化架构。{x+1=1}x:=x+1{x=1}P=X=1个

(还有其他一些。我对于省略指称语义特别难过,但是这个答案已经很长了。)机器代码和操作语义与大多数人称为“真实程序”的内容非常接近。这是一篇开创性的论文,它碰巧对DEC Alpha机器代码的一个子集使用了操作语义:

为什么要使用公理化的高层语义?当您不希望正确性证明取决于您所运行的硬件时。然后,该方法是相对于一些方便的高级语义证明算法的正确性,然后证明相对于更接近于实际机器的低层语义而言,语义是合理的。

总而言之,我可以想到导致您提出问题的三个原因:

  1. 您只看到了高级语义,这些语义看起来不像您用来调用程序的语义,并且您想知道是否存在低级语义。答案是肯定的。
  2. 您想知道如何证明模型与现实相对应。就像物理学中一样,您不需要。您只需提出更好的模型并对照实际情况进行检查。
  3. 您尚未看到语法和语义之间的区别,以及为程序分配含义的各种方法。 两个问题列出了一些书。

这个答案只是试图确定我理解问题的三种不同方式。在深去任何这些问题将需要很大的空间。


8

减少程序与其规范之间差距的一种方法是使用具有形式语义的语言。一个有趣的例子是Esterel。在GérardBerry的网页上可以看到一些有趣的话题,有关他的工作将形式化方法带入了现实世界。 http://www-sop.inria.fr/members/Gerard.Berry/

ps去过空中客车吗?您已经使用了正式的方法!


1
关于空客如何使用正式方法的任何参考都将有所帮助。(了解其可能的专有信息。)
vzn 2012年

@RossDuncan我在进入Berry的网页并进行了一些搜索之后找到了该网页。这是您指的空中客车公司使用的正式方法吗?
scaaahu 2012年

我没有关于空中客车公司使用Esterel的内部信息;我的评论只是重复贝里在一次演讲中的一句话。但是,此页面吹嘘成功在空客中使用SCADE产品。如果您看看Esterel的历史,它很早就被Dassault所采用。Google是您的朋友。
罗斯·邓肯

2
空中客车公司还使用astree.ens.fr
Radu GRIGore 2012年

7

在“现实世界”中构建可靠软件的科学仍在开发中,并且在某种程度上与固有的文化或人类学研究相吻合,因为计算机和软件不会“引起”错误,而人类却可以!这个答案将集中在一般的Q / A方法上,其中正式软件验证可以看作是一个要素。

一个令人瞩目的观察结果是,通常在市场上,“足够好”却“笨拙”的软件经常会卖出经过更好测试但功能较低的软件。换句话说,市场并不总是重视软件质量,而并不总是强调质量的软件工程现代技术在一定程度上反映了这一点。此外,质量通常会给最终产品增加可观的费用。关于这些警告,以下是一些基本知识:

  • 冗余/容错系统。这是一个广泛的研究领域。容错和冗余可以设计到系统的多个层中。例如路由器,服务器,磁盘驱动器等。

  • 测试。所有类型-单元测试,集成测试,用户验收测试,回归测试等。

  • 如今,通过可在无人看管的情况下运行的测试套件进行自动测试的功能已变得更加成熟/重要。测试套件的运行通常与构建工具结合在一起。

  • 测试中的一个重要概念是 代码覆盖率。即测试执行什么代码。测试无法在代码中找到测试未“触及”的错误。

  • 测试中的另一个关键概念是测试工具,该工具使用不容易直接访问的代码。

  • 测试应该对软件的所有级别进行测试。如果软件被很好地模块化,这并不困难。更高级别的测试应深入到代码中。使用少量测试设置即可执行大量代码的测试 “测试杠杆”

  • 编写代码 对于测试而言,尽可能地复杂最少是很重要的。测试应该是体系结构设计中的考虑因素。通常,有多种方法可以实现相同的功能,但有些方法对测试覆盖率/杠杆作用有很大不同。对于代码中的每个分支,通常是另一个测试用例。分支内的分支升级,代码路径呈指数增长。因此,避免高度嵌套/有条件的逻辑可提高测试能力。

  • 读书 著名的(大规模)软件故障,其中有许多示例和案例研究有助于理解历史并发展面向质量考虑的思维方式。

  • 一个人就可以摆脱测试!两者都有问题测试太少或太多。有一个“最佳地点”。两种极端情况都无法成功构建该软件。

  • 以最有效的方式使用所有基本工具。调试器,代码分析器,测试代码覆盖工具,缺陷跟踪系统等!不一定要修复,而是要跟踪即使在跟踪软件最小的缺陷。

  • 谨慎使用SCM,源代码管理和分支技术对于避免回归,隔离和进行修复等很重要。

  • N版本编程:一种经常用于开发关键任务软件的实践。这种做法的前提是,N个独立开发的程序不太可能具有相同的常见错误/错误。几篇论文对此进行了批评。但是,NVP不是一种理论概念,而是一种实践。

我相信物理学家费曼(Feynman)在他的书“您在乎别人的想法?”中对NASA用来保证航天飞机系统可靠性的方法有所了解。—他说他们有两个团队,例如A团队和B团队。A团队开发了该软件。B团队对A团队采取对抗性方法,并试图破坏该软件。

如果B团队具有良好的软件工程背景,那将很有帮助,即他们自己可以编写代码管理/编程测试等。在这种情况下,B团队的资源水平几乎与A团队相同。这种方法很昂贵,因为它可以使构建软件的成本几乎翻倍。与开发团队相比,质量检查团队通常较小。


8
对于按Shift键和字母会产生大写字母的规范,应该检查您的操作系统是否正确。
Andrej Bauer

1
附录:进度限制会影响质量。另请参见由范围,成本,进度和质量组成的项目管理三角形,这是受所有方面影响的“区域”。3.另请参见“为什么IT行业不能像其他行业一样迅速交付无缺陷的大型项目?” 。我自己没有添加N版本的内容(它涵盖了其他答案),但请注意Feynman提到NASA在航天飞机设计中也使用了该版本。
vzn 2012年


1
另一个有趣的案例研究是火星漫游者,它有大量的代码,其中大部分是自动生成的。在那种情况下,先前的漫游者现场测试了大多数软件,并且软件被重用了。
vzn 2012年

6

一种旧方法(但仍在某些应用程序中使用)是N版本编程

从维基百科:

N版本编程NVP),也称为多版本编程,是软件工程中的一种方法或过程,其中多个功能等效的程序是根据相同的初始规范独立生成的。N版本编程的概念是由Liming Chen和Algirdas Avizienis于1977年提出的,其主要猜测是“独立于编程工作将大大减少在两个或多个版本的程序中出现相同软件故障的可能性”。 NVP的目的是通过建立容错或冗余来提高软件操作的可靠性。
....

参见例如:“ 建筑物故障的挑战-民用飞机的容错飞行控制系统


值得注意的是,n版本编程不起作用。基本假设-即软件开发过程的反复试验中的错误是独立的- 完全错误。这个想法从理论上讲是没有道理的(很难实现的算法对于第二个独立团队来说再简单不过了),并且也在实验中被揭穿:John Knight和Nancy Leveson的实验表明独立性假设在统计上是无效的是软件工程领域最著名的论文之一。
Neel Krishnaswami 2014年

@NeelKrishnaswami:我同意!但是我认为(但我不是专家)不起作用的应该替换为它,与其他方法相比,它不能像应有的那样提高可靠性。引用K&L的话:“ ...我们从来没有建议我们将自己的结果用作决定N版本编程有效性的基础。我们只是建议谨慎行事…… ”。我认为关于NVP方法对关键系统设计有用多少的争论仍然悬而未决(请参阅Khoury等人的最新工作)
Marzio De Biasi 2014年

4

fajrian,您完成的这个问题涵盖了软件工程师研究中的两个最大问题:规范与模型之间以及模型与代码之间的一致性。这里的模型表示系统将要执行的操作或将如何执行的表示,有很多层次可以对系统进行建模。

因此,有些人试图为您的问题找到最佳答案。因为很难基于模型(例如,使用形式化方法)检查​​软件的正确性。我知道JML是一种实现方法,但我不知道其用法的局限性。

总结起来,如何难以检查代码的正确性,人们尝试混合使用正式方法和测试,例如根据规范自动创建测试。实时系统的一个例子是TIOSTS,它基于输入/输出定时事件。

仅测试不是一种正式的方法,这样做可以提高可靠性,但不能检查正确性。


3

两三年前,我开始研究应用于软件的形式化方法。这是出于好奇心以及我必须学习较长时间的编程工具和方法的驱动。虽然我梦wish以求地梦想着银子弹,但我确实认为这个问题没有答案:“如何编写正确的程序?”。

在尝试了一些工具(Z,B,VHDL和Estelle)之后,此刻,我正在使用TLA +。这是时态逻辑的一种变体,带有用于模型检查和力学证明的软件工具。我认为我之所以选择这种方法,是因为L. Lamport支持它,语法很简单,有很多示例,有社区来照顾它,并且语言和工具都有很好的文档记录。

关于我的第一个问题,我认为没有完整的答案。但是,值得学习的是,正式指定系统的某些部分是有回报的。对一些复杂的项目进行反向工程也非常有用。也就是说,为困难和关键部分创建蓝图是有效的。但是,我认为没有一种有效的方法可以自动将规范转换为编程语言或框架(除非您将项目限制在非常特定的环境中)。我也不认为拥有正式的规范会阻止您测试软件。

简而言之,我认为下面的比喻(来自Lamport)非常有力:“您是否期望房屋是根据蓝图自动建造的?您会购买尚未建造且没有蓝图的房屋吗?” 。

在执行此任务期间,我发现以下有用的资源:

  • 软件规范方法。本书对现有方法和工具进行了广泛概述。在这里,您可以找到Z,SDL,TLA +,Petri网,Coq等的基本说明和示例。
  • 如果您认为TLA +可以满足您的需求,那么我真的建议您使用《指定系统》一书。您可以免费获得该书,并附带了可以使用的示例:)。
  • 最近,我阅读了几篇相关的文章,它们对形式方法的最新发展提出了两种不同的观点:形式方法论形式验证数学

祝好运!


1

到目前为止,答案已经涵盖了有关规范和代码之间如何相互联系的基础的大部分内容。我只想在此线程的头中添加一个更实用的方法来解决该问题:

如何创建关键任务软件?

有一些工具可以自动分析您的代码中是否存在错误(违反规范或“典型错误”)。据我所知,这些方法主要基于静态分析,与您提到的理论(LTL / CTL / ...)没有直接关系,但是它们确实在实际代码中发现错误,并且从实际角度出发已经是可行的。认为,在工业项目中使用此类工具。我个人没有使用过很多工具,但是这些工具似乎开始被从业人员接受。为了进一步阅读,我可以推荐以下博客文章:

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/


Java,开源apache的示例实现
vzn 2012年

0

在构建关键任务软件时,验证算法可能会很有用。

证明算法是一种算法,该算法会针对每个输出产生一个证明或见证(易于验证的证明),证明该特定输出未受到漏洞的破坏。

在此调查文件中阅读更多内容由RM McConnell,K。Mehlhorn和S.Naher和P.


1998年,Pnueli,Siegel和Singerman以翻译验证的名称描述了将该思想应用于编译器编译器本质上是高阶的(输入是程序,输出是程序),因此它们往往难以验证。但是还是有像X. Leroy这样疯狂的人,他们无论如何都会开发经过验证的编译器。(在最佳意义上疯狂!)
Radu GRIGore 2012年

-2

但是,然后如何验证软件(不仅是规范)确实正确?

单元测试?为规范中的每个需求编写一个测试,然后测试实现中的每个方法以查看其输出/输入是否符合所述规范。这可以是自动化的,因此这些测试可以连续运行,以确保任何更改都不会破坏以前的工作功能。

从理论上讲,如果您的单元测试具有100%的代码覆盖率(即,测试了代码中的每个方法),则软件应该是正确的,前提是测试本身是准确而现实的。


5
对于任何相当复杂的程序,代码覆盖范围(通过测试)不能确保正确性。您将不得不涵盖所有可能的处决;所有代码行是足够的。
Radu GRIGore 2012年

1
代码覆盖范围太模糊了一个概念。我们区分例如方法覆盖率,语句覆盖率,分支覆盖率,路径覆盖率等。正如Radu所指出的,对于非平凡的程序,测试经常会遇到组合爆炸。就是说,航空软件拥有良好的记录,其正确性通常基于广泛的测试。
Martin Berger

如果要使用JUnit之类的工具进行测试,则这种标准的自动测试不能涵盖所有情况(除非程序非常小)。对于典型的应用程序,这种测试通常就足够了。但是对于关键任务应用程序,我不知道这是否足够(或不够)。
fajrian 2012年

2
@vzn:根据我的经验,在学者和从业者之间分别认为是一个错误。此外,我敢打赌,我的大多数(前)行业同事会同意“测试代码中的每个方法”听起来并不令人放心。(而且,不,我没有拒绝投票。我几乎从来没有
投票

1
@vzn:我是否说过你否则?我只是在试图解释为什么我相信其他人没有赞成这个答案。目前我无法回答这个问题,因为我不理解。
Radu GRIGore 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.