全文约6,500字,建议收藏阅读
作者:刘向
出品:汽车电子与软件
引 言
持久性是自适应平台中的核心功能,负责提供安全可靠的存储解决方案。它抽象底层文件系统,让应用程序能统一存储和访问数据,同时实施访问控制和资源管理。
由于自适应应用程序不能直接访问文件系统,持久性功能集群作为平台抽象层,通过C++ API(位于ara::per命名空间)支持文件存储和键值数据库存储,帮助应用程序跨驾驶周期保存状态和管理配置,无需担心底层复杂性。
#01
持久性(Persistency)是什么?
持久性是Adaptive Platform架构中的一个功能集群(Functional Cluster)。持久性功能集群允许自适应应用程序访问底层文件系统,这主要有两个原因:
1) 持久性功能集群使平台能够对存储实施访问控制和资源管理;
2) AUTOSAR为自适应应用程序支持的PSE51子集不允许直接访问文件系统,因此需要一个平台抽象层。自适应应用程序可以使用持久性功能集群来跨多个驾驶周期存储状态、访问配置数据等。
Persistency模块明确了标准化定义,涵盖两大方面:
1. API接口:提供C++ API(位于ara::per命名空间),支持数据存储、更新、删除及查询,数据格式可选平面文件或键值数据库。
2. 配置管理:允许用户通过配置文件自定义持久化行为,包括存储后端选择、存储周期设定及数据清理策略。
图5.1 直观展示了Persistency模块如何通过键值存储接口与其他功能集群进行交互。
图5.2 则详细说明了Persistency模块的文件存储接口如何服务于AUTOSAR自适应平台内的其他功能集群。
#02
持久性功能集群的职责
持久性功能集群负责管理自适应应用程序对底层文件系统的访问,使自适应平台能够控制对存储的访问,从而实施访问控制和资源管理。
2.1 文件系统访问
持久性提供了两种平台抽象,使自适应应用程序能够访问文件系统:
- 文件存储:通过自适应应用程序的端口原型<PortPrototype>直接读写文件。文件存储生成一个“文件数组”,这是AUTOSAR元素,定义了一组单个文件。文件数组是部署端与<PersistencyFileStorageInterface>对应的部分。
- 键值数据库:支持键值对的平面数据库。
2.2 访问控制
AUTOSAR为持久性定义了访问控制(特别是并行访问)。持久性数据永远不会在两个或多个进程之间共享,因此始终是单个进程本地的。键值数据库和文件数组在部署期间映射到进程和端口原型。
在部署过程中,每个映射只能定义一个进程;如果需要在进程之间共享数据,应使用功能集群通信管理。然而,持久性数据可以在同一进程上下文中运行的多个线程之间共享。有两种方式可以创建共享访问:
- 通过OpenKeyValueStorage和OpenFileStorage返回的SharedHandle可以复制到另一个线程;
- 可以在独立线程中为相同的KeyValueStorage或FileStorage调用OpenKeyValueStorage或OpenFileStorage API。OpenFileReadWrite、OpenFileReadOnly和OpenFileWriteOnly返回的UniqueHandle保证了访问限制。
由于键值数据库操作的原子性,多个线程可以同时访问KeyValueStorage实例中的单个键。然而,由于无法同步对单个文件的读写访问,多个线程无法同时访问FileStorage实例中的单个文件。
2.3 资源管理
- 存储大小:可以为KeyValueStorage或FileStorage配置资源使用的上限和下限(以字节为单位)。可以使用<PersistencyDeployment.maximumAllowedSize>和<PersistencyDeployment.minimumSustainedSize>分别定义资源使用的上限和下限。
- 内存堆:可以使用SDG参数KeyValueStorageMemorySpace配置为KeyValueStorage分配的内存大小(以kB为单位)。可以使用SDG参数FileStorageOpenFiles配置FileStorage元素中同时打开的文件的最大数量。
2.4 数据一致性
持久性功能集群负责数据一致性和完整性。支持以下特性:
- AUTOSAR冗余处理“M out of N”;
- AUTOSAR冗余处理“CRC”;
- 在打开键值或文件存储时通过创建工作副本实现电源故障安全操作。
2.5 安装与更新
持久性功能集群负责:
- 初始化时安装键值和文件存储;
- 根据更新策略,更新键值和文件存储实例;
- 在集合和元素层面删除数据;
- 将存储回滚至先前版本。
自适应应用程序可通过OpenKeyValueStorage或OpenFileStorage隐式操作,或通过UpdatePersistency显式更新持久性数据。运行时无需管理可执行文件、清单或初始内容文件的更新,这些由更新和配置管理功能集群处理。
#03
配置与工作流程
持久性工作流程涵盖应用程序设计与集成两阶段。文件存储与键值数据库流程相似,差异仅在于元素和属性的命名及类型。
Persistency Workflow for Files
#04
应用程序设计
在应用程序设计阶段,设计师构建自适应软件组件(SWC),这些组件凭借
自适应软件组件中的每个<PortPrototype>都通过接口进行分类。
HADL DSL 的interface
应用程序设计在PersistencyFileStorageInterface中定义文件元素(PersistencyFileElement),在PersistencyKeyValueStorage中定义键值对数据元素(PersistencyDataElement)。
这些接口设计基于SWC的端口原型,设计利用各端口的
#05
集成与部署
集成阶段由集成工程师创建持久性部署,以便在真实文件系统上使用应用程序设计中创建的端口原型和持久性接口。AP工具中包含持久性部署创建向导,简化了集成和持久性部署的创建。
对于应用程序设计中的每个
类似地,对于应用程序设计中的每个<PersistencyKeyValueStorageInterface>,创建一个<PersistencyKeyValueStorage> ARXML 元素;对于每个,创建一个<PersistencyKeyValuePair> ARXML 元素,表示数据库中的单个键值对。
还会创建映射元素:
- 为了将端口原型与文件数组关联,为每个由<PersistencyFileStorageInterface>类型的端口原型创建一个<PersistencyPortPrototypeToFileArrayMapping> ARXML 元素。该映射元素引用:
- 一个<PortPrototype>(及其<PersistencyFileProxyInterface>和<PersistencyFileProxy>元素);
- 一个<PersistencyFileArray>(及其包含的<PersistencyFile>元素);
- 一个<Process>。
- 为了将端口原型与键值数据库关联,为每个由
类型的端口原型创建一个 ARXML 元素。该映射元素引用:
- 一个
(及其 和 元素);
- 一个
(及其包含的 元素);
- 一个
。
在部署期间,必须由执行管理(Execution Management)启动使用持久性的自适应应用程序。这是因为特定的进程被映射到<PortType>,这意味着只有执行管理启动的进程才能访问数据。如果应用程序是手动启动的,它将具有不同的进程 ID,因此无法访问数据。
另一个要求是必须指定 FlatCFG 文件。在生成过程中,将为每个使用持久性的进程创建一个特定的 FlatCFG 文件。这些文件需要存在于目标设备上;在执行管理配置中,必须为进程指定包含这些文件的目录。
#06
文件存储访问
持久性实现支持通过由<PersistencyFileStorageInterface>类型的端口原型映射到文件数组的直接文件访问。
自适应应用程序使用FileStorage类(对象工厂)创建读写访问器类,然后使用这些类访问文件数组中的单个文件。访问类的结构如图所示。
FileStorage对象工厂实例是唯一支持的实例化ReadAccessor和ReadWriteAccessor类的方法。FileStorage类不能直接实例化,而是通过OpenFileStorage函数创建。OpenFileStorage函数的参数是指定端口原型
Persistency File Access Class Hierarchy (AUTOSAR R21-11)
创建后,FileStorage对象工厂可用于创建文件访问器类ReadAccessor和ReadWriteAccessor的实例。这些访问器分别用于读取或写入文件。创建访问器类实例的函数的参数filename是为FileStorage配置的文件数组中的条目名称。
#07
键值数据库
持久性功能集群支持键值数据库,可用于对文件中的信息进行结构化存储。
键值数据库存储多个数据项,每个数据项都是一个键值对。持久性 API 提供了操作数据库中键值对的机制,包括通过已知键查找值、查找数据库中存在的所有键以及更新与键关联的值。
KeyValueStorage类不能直接实例化,而是通过OpenKeyValueStorage函数创建。OpenKeyValueStorage函数的参数是一个InstanceSpecifier,它引用一个部署,该部署定义了创建和管理键值数据库及其冗余副本的文件夹路径。
OpenKeyValueStorage返回的值是Result
7.1 原始数据
对于原始数据(例如整数),可以直接使用KeyValueStorage类的方法访问键值对,例如:
KeyValueStorage类支持为每种标准 AUTOSAR 数据类型提供重载方法。
7.2 复杂数据
持久性还支持复杂数据形式的Vector,这是由于GetValue和SetValue方法作为类模板的实现,并通过KeyValueStorage类中的额外重载GetValue和SetValue方法支持。
#08
数据冗余与恢复
持久性通过实现以下 AUTOSAR 概念来确保数据完整性:
- 冗余处理 CRC
- 冗余处理 M out of N
AUTOSAR 冗余处理是可选的,并且可以在同一个自适应应用程序中使用这两种方法。
冗余配置分为两个阶段:
- 应用程序设计阶段:应用程序开发人员可以使用<PersistencyInterface.redundancy>为选定的存储启用或禁用冗余。
- 部署阶段:系统集成人员可以使用<PersistencyDeployment.redundancyHandling>为每个存储单独配置所需的处理和范围。范围可以是集合级别(例如,冗余处理应用于整个存储)或元素级别(例如,冗余应用于键值对或单个文件)。
注意:
- 存在关于单个键/值和FileStorage元素恢复的已知限制。
- 冗余处理的范围被忽略。
8.1 冗余处理 CRC
使用 CRC 的冗余处理使自适应应用程序能够检测数据完整性问题。当调用OpenKeyValueStorage时,键值存储的内容会从文件系统中完全读取,并保证立即检测并报告潜在的数据完整性错误。文件存储的情况则不同,自适应应用程序必须额外评估用于访问文件的函数的返回值,因为错误只能在读取期间检测到。
CRC 算法通过<PersistencyRedundancyCrc.algorithmFamily>和<PersistencyRedundancyCrc.algorithmLength>配置。
8.2 冗余处理 M out of N
启用M out of N冗余处理时,持久性系统会保存原始文件及其N个冗余副本,这是确保数据可恢复的关键。此机制增强了数据鲁棒性,提高了数据损坏后的恢复能力,但相应地也增加了存储需求。在实施冗余支持时,系统集成人员需考虑以下因素:
- 存储容量的增加需求。
- 存储寿命的潜在缩短,特别是非易失性存储器因频繁写入而加速老化。
- 运行时性能的影响,包括初始化时间延长和访问速度减缓。
冗余的配置通过 ARXML 元素<PersistencyRedundancyMOutOfN.m>和<PersistencyRedundancyMOutOfN.n>完成。
下面列举了一些常见的CRC算法
8.3 恢复
当启用 M out of N 冗余处理时,持久性将尝试从冗余副本中恢复数据。实现了两种不同的恢复方法:隐式恢复和尽力恢复。
8.3.1 隐式恢复
当应用程序调用OpenKeyValueStorage或OpenFileStorage并且有足够的冗余信息可用于恢复时,持久性将向自适应应用程序提供纠正后的数据,而无需进一步通知。在下一次写操作(SyncToStorage或SyncToFile)时,损坏的实例将被替换为纠正后的信息。
如果没有足够的冗余信息可用于安全恢复,打开函数将返回kValidationFailed错误。
8.3.2 尽力恢复
当隐式恢复失败时,自适应应用程序可以尝试执行尽力恢复,这意味着持久性将再次尝试恢复持久存储的数据。如果无法恢复,则将从初始值恢复内容(如果未定义初始内容,则为空)。
尽力恢复必须通过调用RecoverAllFiles、RecoverFile或RecoverKeyValueStorage显式触发。
8.4 持久性数据的安装、更新和删除
数据的安装、更新、删除或回滚操作,均依据当前
若应用程序已注册回调(通过RegisterApplicationDataUpdateCallback),则每当存储更新时,该回调都会被触发调用。
8.4.1 安装
若
AP工具通常支持为键值和文件存储设置初始内容,这些内容可在应用程序设计或部署阶段定义,甚至可两者结合使用(部署配置优先)。若未设定初始内容,则仅生成空文件。初始化过程中会考虑更新策略,但策略为DELETE时会跳过初始化。
对于键值存储的初始内容:
- 应用设计阶段使用<PersistencyDataRequiredComSpec.initValue>配置。
- 部署阶段使用<PersistencyKeyValuePair.initValue>配置。
对于文件存储的初始内容:
- 应用设计阶段通过<PersistencyFileProxy.contentUri>指定。
- 部署阶段通过<PersistencyFile.contentUri>指定。
其中,
8.4.2 更新
如果在调用OpenKeyValueStorage、OpenFileStorage或UpdatePersistency时,实际的<SWCluster.Version>高于持久化值,持久性功能集群将执行更新。在更新之前,会创建受影响存储的备份,以便允许数据回滚。
在更新期间,持久性功能集群会比较更新后的清单内容与现有存储的内容,并根据定义的更新策略执行更新。更新策略在应用程序设计和部署阶段的配置中定义。
在每个配置阶段,更新策略可以在以下级别配置:
- 集合级别:适用于整个键值存储或文件存储。可能的值在
中定义:
- DELETE
- KEEP_EXISTING
- 元素级别:仅适用于单个键值对或文件。可能的值在
中定义:
- DELETE
- KEEP_EXISTING
- OVERWRITE
说明:
- “X”:更新策略已定义
- “-”:更新策略未定义
- 部署阶段配置中的集合级别目前是强制属性,并将覆盖应用程序设计阶段的配置。
所选的更新策略按以下方式应用:
- DELETE:删除元素。
- KEEP_EXISTING:保持不变,更新清单中提供的初始内容不会被安装。
- OVERWRITE:使用更新清单中提供的初始内容覆盖现有元素。
8.4.3 回滚
如果在调用OpenKeyValueStorage、OpenFileStorage或UpdatePersistency时,实际的
#09
使用持久性功能
本节介绍如何使用 ISOLAR-VRTE 配置 RTA-VRTE Starter Kit 的持久性功能。
9.1 配置文件存储和键值存储
创建持久性文件存储FileStorage和KeyValueStorage键值存储的第一步是在应用程序设计(.hadl)中定义需求。
首先,需要使用 HADL 的interface命令定义键值存储和文件存储的持久性接口。这些接口定义了使用该接口的任何应用程序的持久性存储属性。例如:
在定义接口后,可以在自适应软件组件(SWC)中使用端口原型(Port Prototype)来访问这些持久性存储。例如:
一旦定义了新的软件组件(Software Component),必须使用执行编辑器(Execution Editor)将其分配到建模的
1. 选择可执行文件(Executable):
- 打开执行编辑器。
- 在模型树中选择目标
,这是与自适应应用程序关联的可执行文件。
2. 分配软件组件原型(SW Component Prototype):
- 在
的属性视图中,找到与软件组件原型(SW Component Prototype)相关的配置项。
- 通过下拉菜单选择之前创建的软件组件原型(例如MyComponent),将其分配到当前的可执行文件中。
3. 映射到进程(Process):
- 确保软件组件原型已正确映射到目标
。每个 代表一个独立的运行时实例,只有映射到该进程的组件才能访问其持久性数据。 - 如果未正确映射,应用程序在运行时将无法访问持久性存储。
4. 配置进程环境变量:
- 在部署阶段,确保为目标进程配置了正确的环境变量,例如ECUCFG_ENV_VAR_ROOTFOLDER,以指定持久性数据存储的根目录。
- 这些配置确保持久性功能集群能够正确访问和管理文件存储和键值存储。
5. 生成和部署:
- 完成配置后,使用 ISOLAR-VRTE 生成部署文件(如ARXML和 FlatCFG)。
- 将这些文件部署到目标系统,并确保执行管理(Execution Management)能够正确启动映射的进程。
示例:
假设有一个名为MyComponent的软件组件,它通过端口原型访问持久性存储。在执行编辑器中,选择目标
注意事项:
- 确保每个
只能映射一个软件组件原型,以避免资源冲突。
- 如果需要在多个进程之间共享数据,应使用功能集群通信管理(Functional Cluster Communication Management)而不是持久性功能集群。
- 部署后,验证执行管理是否正确启动了目标进程,并检查持久性存储的访问权限和资源限制是否按预期工作。
接下来,需要使用持久化部署创建向导将持久化部署映射到
端口名称是应用程序设计的一部分,应在应用程序源代码中使用,因为它们不受任何集成映射的影响。为了方便使用,端口名称和文件名可以在源代码中定义为常量:
9.2 文件存储
9.2.1 访问文件存储
要打开一个文件存储(`FileStorage`),需要使用 `OpenFileStorage` 函数,并传入一个 `InstanceSpecifier` 参数,该参数表示为文件存储实例定义的端口名称。
一旦文件存储被打开,需要创建一个文件访问器。访问器的类型取决于是否需要写入文件存储的权限:
读写权限:使用 `OpenFileReadWrite` 方法创建 `ReadWriteAccessor`。
只读权限:使用 `OpenFileReadOnly` 方法创建 `ReadAccessor`。
9.2.2 写入文件存储
要向文件存储写入数据,需要创建一个 `ReadWriteAccessor`。然后可以使用 `WriteText` 方法写入消息。
最后,必须使用 `SyncToFile` 方法将写入文件存储的任何值同步到文件中。
9.2.3 从文件存储中读取
要从文件存储中的文件读取数据,需要先访问文件(通过 ReadWriteAccessor 或 ReadAccessor),然后才能使用 ReadLine 方法。
9.3 键值存储
9.3.1 访问键值存储
首先,必须使用 OpenKeyValueStorage 函数打开键值存储:
9.3.2 写入键值存储
要将键值对写入键值存储,需使用 SetValue 方法。
在存储设备上,键值存储文件以非人直接可读的格式写入,但可以看到键值对写入的结果。
#10
小 结
Adaptive AUTOSAR的Persistency模块为自适应应用程序提供了强大的持久化数据存储能力。通过支持键值存储和文件存储两种类型以及提供丰富的功能特性(如数据完整性检查、安全存储和资源统计信息等),Persistency模块确保了数据的可靠性、安全性和可管理性。随着汽车电子技术的不断发展,Persistency模块将在未来发挥更加重要的作用,为自适应应用程序提供更加高效、灵活和安全的持久化数据存储解决方案。
-end-
本专栏是由汽车电子与软件打造的中立性技术科普专栏,将系统地阐述软件定义汽车下的关键挑战和工程实践。欢迎订阅本专栏!