如何开展软件架构设计
写在前面:
软件架构设计是软件功能安全开发流程中非常重要的一项活动,它起到承接软件安全需求,将软件安全需求分配到具体的软件组件中去,引导软件详细设计的实现,并指导软件开发工程师来完成代码逻辑的开发的作用。同时,软件架构设计也是开展软件集成测试的重要输入文件。
软件开发的V模型流程图
标准ISO 26262 Part6明确给出了软件架构设计的活动目标,输入物以及输出产物的要求:
其中ISO 26262, Part6-7.1给出了软件架构设计的目标:
a) 开发满足软件安全需求和其他软件需求的软件架构;
b) 验证软件架构设计适用于满足相应ASIL等级的软件安全需求;
c) 支持软件的实现和软件的验证。
ISO 26262, Part6-7.3.1&7.3.2阐述了软件架构设计的必要输入物:
a)软件开发环境的文档;
b)细化的HSI 接口文档;
c)软件安全需求;
d)技术安全概念;
e)系统架构设计;
f)可用的已认证的软件组件(之前未按照ASIL等级开发,但是已经经过认证);
g)非安全相关的功能规范,软件属性和其他的软件要求。
ISO 26262, Part6-7.5要求软件架构设计的输出产物应包括:
a)软件架构设计规范;
b)软件安全分析;
c)软件DFA;
d)软件验证报告。
那究竟该如何来进行软件架构设计呢?软件架构设计需要阐述清楚软件组件的哪些要素呢?在软件组件设计中需要关注到哪些设计原则呢?本文基于标准的要求,希望将上述问题都能够解释清楚,供各位从事于功能安全软件开发的小伙伴参考,并欢迎针对文章中的细节问题开展进一步的深入交流。
一、软件架构的设计原则
简而言之,软件架构设计就是根据软件安全需求,分解软件功能,定义好各个功能所需要的软件组件以及软件组件之间的交互接口。通过一个好的软件架构设计,就可以清楚地知道各个功能、各个软件组件之间的交互关系,知道数据流怎么走,并一眼看出不同模块间的层次、逻辑和时序关系。这样,软件工程师只要根据软件组件设计方案编写代码,不同软件组件组合起来后系统就可以正常工作。
为了达到上述目标,软件架构设计需要满足一系列的约束条件和设计原则。
标准ISO 26262 Part6根据不同ASIL等级,对功能安全相关的软件架构设计提出了相关约束,包括:
1、软件架应能够承载软件安全需求。
2、软件架构设计原则,包括:
(1)适当分层;
(2)限制软件组件规模和复杂度;
(3)限制接口规模;
(4)组内高内聚;
(5)组间低耦合等。
3、软件架构设计验证方法,包括: 设计走查,设计检查,控制流、数据流分析,仿真等。
4、硬件安全需求应被分配至软件架构中的相关组件。
5、不同或非ASIL等级软件组件开发需满足以下原则之一:
(1)按最高ASIL等级;
(2)要素共存FFI。
6、对软件安全需求和具体实施之间要保证设计的可追溯性。
1.1 分层,使用层级结构
使用层级结构的目的是通过限制子系统的局部复杂度在一定范围内,从而达到使层次结构合理的要求。其主要思路是将软件系统按照不同的职责划分为多个层次,每个层次之间通过接口进行通信,实现模块之间的解耦和复用,提高系统的可维护性、可扩展性和可重用性。AUTOSAR就是一种被普遍接受的分层结构。
1.2 限制软件组件规模和复杂度
可通过减少模型中的重复模块或者使用参考模型来达到。
1.3 限制接口规模
使用总线结构是一种常见的用于减少接口数量的方法。
要求2.1,2.2和2.3都是从降低单个软件组件的复杂度考虑的,降低组件复杂度可以降低代码编写的难度,减少代码实现过程中可能引入的人因错误,同时也能够简化测试用例的复杂度。
可以用一些量化的指标来约束软件组件的复杂度,例如圈复杂度(Cyclomatic complexity)。
1.4 组内高内聚和组件低耦合
内聚、耦合是用于描述不同软件组件间的设计原则的。
内聚,是描述一个模块内各元素彼此结合的紧密程度,是从功能角度来度量模块内的联系。内聚共有7种方式,不同内聚方式的程度有所区别。
● 偶然内聚,一个模块内的各元素之间没有任何联系,仅是恰好放在同一个模块内。
● 逻辑内聚,把几种相关的功能组合在一起,由调用方传入的参数来确定具体执行哪一种功能。
● 时间内聚,指一个模块内的组件除了在同一时间都会被执行外,相互之间没有任何关联。
● 过程内聚,指一个模块内的组件以特定次序被执行,但相互之间没有数据传递。
● 通信内聚,指一个模块内的组件以特定次序被执行,且相互之间传递和操作相同的数据。
● 顺序内聚,指一个模块内的元素以特定次序被执行,且上一步的输出被下一元素所依赖。
● 功能内聚,指一个模块内所有组件属于一个整体,完成同一个不可切分的功能,彼此缺一不可。
耦合,是描述模块(系统/模块/类/函数)之间相互联系(控制/调用/数据传递)紧密程度的一种度量。耦合也有7中不同的方式。
● 非直接耦合,两个模块之间没有直接关系,它们之间的联系完全是通过主模块控制调用来实现的,这种耦合的模块独立性最强。
● 数据耦合,一个模块访问另一个模块时,彼此之间是通过数据参数(不是控制参数、公共数据结构或外部变量)来交换输入、输出信息的。
● 印记耦合,模块之间使用复合数据结构进行通信时。
● 控制耦合,一个模块通过传送开关、标志等控制信息,明显地控制选择另一模块的功能。
● 外部耦合,指多个模块同时依赖同一个外部因素(IO设备/文件/协议/DB等)。
● 共用耦合,指不同的模块共享全局数据的信息(全局数据结构、共享的通信区、内存的公共覆盖区)。
● 内容耦合,在低级语言(汇编)中出现,高级语言从设计上已避免出现内容耦合。
通常来说,模块内的元素的职责相关性低,通常也意味着模块与外部是紧耦合的,软件组件设计呈现“低内聚、高耦合”的特点;相反,如果模块内的元素的职责相关性强,也意味着模块与外部是松耦合的,即“高内聚、低耦合”。在软件组件设计过程中,解决了耦合的问题,也就解决了内聚的问题。
如果代码是低内聚和高耦合的,其弊端很明显,如果修改一个逻辑,会导致多处代码要修改,可能影响到多个业务链路,这增加了出bug的业务风险,同时增加了测试回归的范围,导致研发成本增加。
总结:因此软件设计的总体原则是降低每个软件组件的复杂度,追求软件组件间的高内聚和低耦合。
二、软件架构要素的静态设计要求
标准ISO 26262 Part6 – 7.4.5针对软件架构要素的静态设计提出了要求,包括:
● 分级层次的软件结构;
● 数据类型和它们的特征参数;
● 软件组件的外部接口;
● 嵌入式软件的外部接口;
● 全局变量;
● 包括架构的范围和外部依赖的约束。
总结:软件架构中静态设计关注的是软件组件的相关项,比如组件、输入、输出、端口(port)、接口(interface)、约束条件等。
三、软件架构要素的动态设计要求
标准ISO 26262 Part6 – 7.4.5针对软件架构要素的静态设计提出了要求,包括:
● 事件和行为的功能链;
● 数据处理的逻辑顺序;
● 控制流和并发进程;
● 通过接口和全局变量传递的数据流;
● 时间的限制。
总结:软件架构中动态设计关注的是软件组件相关项的控制逻辑和相互关系,是对静态要素的组合描述。
四、软件架构设计视图
为了描述软件架构静态和动态特性,ISO 26262对软件架构设计的标记法进行明确规定,包括: 自然语言,非半形式,半形式(伪代码,UML, SysML,Simulink等),形式记法(可运行代码)。其中对于ASIL等级C和D软件安全需求对应的架构设计强烈推荐采用半形式法进行。
可以使用结构视图:来描述架构静态结构和接口。常见的结构视图包括,类图,组件图,包图等。
● 类图 —— 描述系统中的类,以及各个类之间的关系的静态视图。
● 组件图 —— 由组件 + 接口 + 关系 + 端口 + 连接器组成。
● 包图 —— 描述的是模型中的包及其包含的元素组合。