我正在自我学习形式方法。我听说使用(通常仅使用)正式方法来创建任务关键型软件(例如核反应堆控制器,飞机飞行控制器,空间探测器控制器)。这就是为什么我有兴趣学习它的原因:p
但是,在学习了形式化方法(尤其是LTL,CTL及其同级方法)之后,我觉得它们只能用于验证规范的正确性(安全性,活跃性,公平性等)。
但是,然后如何验证软件(不仅是规范)确实正确?
免责声明:在理论计算机科学方面,我是90%的白痴。因此,请在回答时保持仁慈。
我正在自我学习形式方法。我听说使用(通常仅使用)正式方法来创建任务关键型软件(例如核反应堆控制器,飞机飞行控制器,空间探测器控制器)。这就是为什么我有兴趣学习它的原因:p
但是,在学习了形式化方法(尤其是LTL,CTL及其同级方法)之后,我觉得它们只能用于验证规范的正确性(安全性,活跃性,公平性等)。
但是,然后如何验证软件(不仅是规范)确实正确?
免责声明:在理论计算机科学方面,我是90%的白痴。因此,请在回答时保持仁慈。
Answers:
这个问题相当广泛。为了在一个合理的空间内回答这个问题,我将作很多简化。
让我们就术语达成一致。当一个程序暗示其规范时是正确的。通过确定确切是什么程序和确切是什么规范,可以用许多方式使这个模糊的语句精确。例如,在模型检查中,程序是Kripke结构,而规范通常是LTL公式。或者,该程序可以是PowerPC指令的列表,而规范可以是用一阶逻辑编写的一组Hoare-Floyd断言。有很多可能的变化。很容易得出结论,在一种情况下(Kripke结构)我们不验证实际程序,而在第二种情况下(PowerPC指令列表)我们进行验证。但是,重要的是要意识到我们确实在两种情况下都在研究数学模型,这很好。(这种情况与物理学非常相似,例如,经典力学是现实的数学模型。)
大多数形式化区分程序的语法和语义。也就是说,它的表示方式及其含义。从程序验证的角度来看,程序的语义至关重要。但是,当然,重要的是要有一种明确的方法来为程序的(语法表示)分配含义。以下是两种流行的方式:
(还有其他一些。我对于省略指称语义特别难过,但是这个答案已经很长了。)机器代码和操作语义与大多数人称为“真实程序”的内容非常接近。这是一篇开创性的论文,它碰巧对DEC Alpha机器代码的一个子集使用了操作语义:
为什么要使用公理化的高层语义?当您不希望正确性证明取决于您所运行的硬件时。然后,该方法是相对于一些方便的高级语义证明算法的正确性,然后证明相对于更接近于实际机器的低层语义而言,语义是合理的。
总而言之,我可以想到导致您提出问题的三个原因:
这个答案只是试图确定我理解问题的三种不同方式。在深去任何这些问题将需要很大的空间。
减少程序与其规范之间差距的一种方法是使用具有形式语义的语言。一个有趣的例子是Esterel。在GérardBerry的网页上可以看到一些有趣的话题,有关他的工作将形式化方法带入了现实世界。 http://www-sop.inria.fr/members/Gerard.Berry/
ps去过空中客车吗?您已经使用了正式的方法!
在“现实世界”中构建可靠软件的科学仍在开发中,并且在某种程度上与固有的文化或人类学研究相吻合,因为计算机和软件不会“引起”错误,而人类却可以!这个答案将集中在一般的Q / A方法上,其中正式软件验证可以看作是一个要素。
一个令人瞩目的观察结果是,通常在市场上,“足够好”却“笨拙”的软件经常会卖出经过更好测试但功能较低的软件。换句话说,市场并不总是重视软件质量,而并不总是强调质量的软件工程现代技术在一定程度上反映了这一点。此外,质量通常会给最终产品增加可观的费用。关于这些警告,以下是一些基本知识:
冗余/容错系统。这是一个广泛的研究领域。容错和冗余可以设计到系统的多个层中。例如路由器,服务器,磁盘驱动器等。
测试。所有类型-单元测试,集成测试,用户验收测试,回归测试等。
如今,通过可在无人看管的情况下运行的测试套件进行自动测试的功能已变得更加成熟/重要。测试套件的运行通常与构建工具结合在一起。
测试中的一个重要概念是 代码覆盖率。即测试执行什么代码。测试无法在代码中找到测试未“触及”的错误。
测试中的另一个关键概念是测试工具,该工具使用不容易直接访问的代码。
测试应该对软件的所有级别进行测试。如果软件被很好地模块化,这并不困难。更高级别的测试应深入到代码中。使用少量测试设置即可执行大量代码的测试 “测试杠杆”。
编写代码 对于测试而言,尽可能地复杂最少是很重要的。测试应该是体系结构设计中的考虑因素。通常,有多种方法可以实现相同的功能,但有些方法对测试覆盖率/杠杆作用有很大不同。对于代码中的每个分支,通常是另一个测试用例。分支内的分支升级,代码路径呈指数增长。因此,避免高度嵌套/有条件的逻辑可提高测试能力。
读书 著名的(大规模)软件故障,其中有许多示例和案例研究有助于理解历史并发展面向质量考虑的思维方式。
一个人就可以摆脱测试!两者都有问题测试太少或太多。有一个“最佳地点”。两种极端情况都无法成功构建该软件。
以最有效的方式使用所有基本工具。调试器,代码分析器,测试代码覆盖工具,缺陷跟踪系统等!不一定要修复,而是要跟踪即使在跟踪软件最小的缺陷。
谨慎使用SCM,源代码管理和分支技术对于避免回归,隔离和进行修复等很重要。
N版本编程:一种经常用于开发关键任务软件的实践。这种做法的前提是,N个独立开发的程序不太可能具有相同的常见错误/错误。几篇论文对此进行了批评。但是,NVP不是一种理论概念,而是一种实践。
我相信物理学家费曼(Feynman)在他的书“您在乎别人的想法?”中对NASA用来保证航天飞机系统可靠性的方法有所了解。—他说他们有两个团队,例如A团队和B团队。A团队开发了该软件。B团队对A团队采取对抗性方法,并试图破坏该软件。
如果B团队具有良好的软件工程背景,那将很有帮助,即他们自己可以编写代码管理/编程测试等。在这种情况下,B团队的资源水平几乎与A团队相同。这种方法很昂贵,因为它可以使构建软件的成本几乎翻倍。与开发团队相比,质量检查团队通常较小。
一种旧方法(但仍在某些应用程序中使用)是N版本编程
从维基百科:
N版本编程(NVP),也称为多版本编程,是软件工程中的一种方法或过程,其中多个功能等效的程序是根据相同的初始规范独立生成的。N版本编程的概念是由Liming Chen和Algirdas Avizienis于1977年提出的,其主要猜测是“独立于编程工作将大大减少在两个或多个版本的程序中出现相同软件故障的可能性”。 NVP的目的是通过建立容错或冗余来提高软件操作的可靠性。
....
参见例如:“ 建筑物故障的挑战-民用飞机的容错飞行控制系统 ”
两三年前,我开始研究应用于软件的形式化方法。这是出于好奇心以及我必须学习较长时间的编程工具和方法的驱动。虽然我梦wish以求地梦想着银子弹,但我确实认为这个问题没有答案:“如何编写正确的程序?”。
在尝试了一些工具(Z,B,VHDL和Estelle)之后,此刻,我正在使用TLA +。这是时态逻辑的一种变体,带有用于模型检查和力学证明的软件工具。我认为我之所以选择这种方法,是因为L. Lamport支持它,语法很简单,有很多示例,有社区来照顾它,并且语言和工具都有很好的文档记录。
关于我的第一个问题,我认为没有完整的答案。但是,值得学习的是,正式指定系统的某些部分是有回报的。对一些复杂的项目进行反向工程也非常有用。也就是说,为困难和关键部分创建蓝图是有效的。但是,我认为没有一种有效的方法可以自动将规范转换为编程语言或框架(除非您将项目限制在非常特定的环境中)。我也不认为拥有正式的规范会阻止您测试软件。
简而言之,我认为下面的比喻(来自Lamport)非常有力:“您是否期望房屋是根据蓝图自动建造的?您会购买尚未建造且没有蓝图的房屋吗?” 。
在执行此任务期间,我发现以下有用的资源:
祝好运!
到目前为止,答案已经涵盖了有关规范和代码之间如何相互联系的基础的大部分内容。我只想在此线程的头中添加一个更实用的方法来解决该问题:
如何创建关键任务软件?
有一些工具可以自动分析您的代码中是否存在错误(违反规范或“典型错误”)。据我所知,这些方法主要基于静态分析,与您提到的理论(LTL / CTL / ...)没有直接关系,但是它们确实在实际代码中发现错误,并且从实际角度出发已经是可行的。认为,在工业项目中使用此类工具。我个人没有使用过很多工具,但是这些工具似乎开始被从业人员接受。为了进一步阅读,我可以推荐以下博客文章:
http://www.altdevblogaday.com/2011/12/24/static-code-analysis/
在构建关键任务软件时,验证算法可能会很有用。
证明算法是一种算法,该算法会针对每个输出产生一个证明或见证(易于验证的证明),证明该特定输出未受到漏洞的破坏。
在此调查文件中阅读更多内容由RM McConnell,K。Mehlhorn和S.Naher和P.
但是,然后如何验证软件(不仅是规范)确实正确?
单元测试?为规范中的每个需求编写一个测试,然后测试实现中的每个方法以查看其输出/输入是否符合所述规范。这可以是自动化的,因此这些测试可以连续运行,以确保任何更改都不会破坏以前的工作功能。
从理论上讲,如果您的单元测试具有100%的代码覆盖率(即,测试了代码中的每个方法),则软件应该是正确的,前提是测试本身是准确而现实的。