什么是平台可更新性?
Fuchsia 从内核开始构建,以满足当今不断发展的联网设备生态系统的需求。 在 Fuchsia 平台上构建各种应用和服务,以满足用户需求。产品 产品要求会随时间而变化,因此 Fuchsia 必须能够随之变化以满足这些要求。
新平台版本可能会提供新功能和 bug 修复。与 Fuchsia 平台集成的产品所有者可能需要在新的平台 build 上重新定位其现有产品组件。根据产品的性质和更新传送方式,最终用户甚至可能会直接从 Fuchsia 项目接收平台更新。根据要组装的产品的性质,产品所有者可能无法将现有应用软件移植到新平台版本,并且必须依赖于现有的应用预构建文件继续与新平台预构建文件搭配使用。
Fuchsia 旨在让平台的各个元素能够随时间推移而发生变化和更新,同时支持现有产品和新产品。参与产品生命周期的不同软件供应商可能各自有自己的开发和发布时间表,彼此之间相互独立。本文档介绍了支持这种分离可更新性的机制。
平台可更新性如何运作?
Fuchsia 具有多种机制,可促进平台随着时间的推移进行更新。下面,我们将介绍最常见的机制及其一些应用。
严格定义的接口
接口充当不同软件之间的契约。Fuchsia 定义了平台与其运行的软件之间的此类协定。平台的应用二进制接口 (ABI) 接口已明确定义和枚举。ABI Surface 包括内核的入口点、与平台服务的所有交互,以及其他惯例和协议。开发者可以编写软件来与 Fuchsia 交互,例如使用基于 Fuchsia 集成开发套件 (IDK) 的软件开发套件 (SDK)。Fuchsia IDK 包含接口定义和客户端库,这些接口定义和客户端库提供从同一平台协定派生的应用程序编程接口 (API)。
接口定义语言
Fuchsia ABI 在很大程度上是根据 Fuchsia 接口定义语言 (FIDL) 定义的。
- 内核 vDSO 中的系统调用 (syscall) 和 syscall ABI 以 FIDL 表示。请注意,目前 FIDL 中未定义向系统调用传递和从系统调用传递的结构体。
- 一系列 FIDL 协议用于与用户模式平台服务通信。
- 常用 API 是根据 FIDL 实现的。
FIDL 工具链可以使用多种编程语言为客户端和服务器生成绑定代码。该工具链旨在让您能够轻松扩展对更多语言的支持。
FIDL 旨在支持跨版本兼容性并简化接口更改。FIDL 具有兼容性保证,并会明确列出哪些更改是二进制兼容(ABI 稳定)和/或源代码兼容(API 稳定)。
作为一种接口定义语言,FIDL 具有特殊的功能,可帮助开发者随时间的推移更改协议和类型,同时保持向后和向前兼容性。这有时也称为“柔和转换”。
- 开发者可能会随着时间的推移添加和移除方法。
- 开发者可以使用灵活的联合体类型,让旧客户端忽略新数据。
- 开发者可以重命名类型,而不会破坏 ABI 兼容性。
通过使用 FIDL 定义大部分 Fuchsia 系统接口(尤其是预计会随时间推移而发生变化的部分),Fuchsia 可以更好地利用 FIDL 的特殊功能进行修订。
版本控制和兼容性元数据
Fuchsia 定义了平台版本方案,用于表示 Fuchsia IDK 的 API 级别以及 Fuchsia 平台的 ABI 修订版。Fuchsia IDK 或 Fuchsia 平台的每个版本都可能会引入 API 或 ABI 修订版,在这种情况下,该版本会用增量版本表示。版本化版本可以作为向后/向前兼容期来支持一系列版本。
Fuchsia 接口的属性可以带有相应的版本控制元数据注解。
- 在给定版本中,某个媒体资源已添加,但可能已移除。这会以编号版本的形式为该媒体资源定义支持期限。
- 在给定版本中,某个属性可能会被标记为已废弃。废弃表示最终移除某个属性的意图,但不会影响 ABI。
- 已废弃的属性可能包含以人类可读的形式提供的额外备注。通常,备注会告知开发者在移除媒体资源后应采取哪些措施来避免出现问题。
接口的不同元素可能采用不同的注解方式。例如,在不同版本中,协议中的不同方法或联合体中的字段可能会被添加、废弃或移除。
FIDL 是 Fuchsia 上接口定义的通用语言,支持版本注解。为了帮助更改平台 FIDL 文件的开发者检测其更改是否会导致 API 引入可能不兼容的修订版,系统会从 FIDL 文件生成 API 摘要。您可以将摘要与已保存的参考文件(即金标准文件)进行比较,以找出破坏性更改并确保这些更改是出于明确意图而引入的。
命名空间
传统上,进程是线程和受保护资源(例如虚拟内存区域)的容器。在 Fuchsia 上,进程还可以被分配一个本地命名空间。命名空间是沙盒的基础,可确保程序只能访问其已获授权的资源,并且只能按照授权的方式访问这些资源,例如作为内核对象、FIDL 协议或文件。这些功能构成了程序在运行时可使用的不同capability。
沙盒通常是根据组件清单定义的。一组组件清单可以定义沙盒中存在哪些功能以及如何满足这些功能。不过,沙盒中的组件无法看到这些详细信息。某个组件的父级可以将某个组件提供的功能路由到其子级,从而随着时间的推移更改实现细节,但沙盒的形状保持不变。
沙盒化有助于实现松散耦合,并允许在客户端不知情的情况下更改实现细节。例如,当 Netstack3 取代 Netstack2 时,理想情况下,使用网络功能的组件不会注意到差异。这也有助于测试,因为测试作者可以向组件注入测试双重,而被测组件无法检测到。
包装
软件包是 Fuchsia 上软件分发的单元。软件包包含给定目录布局中的一个或多个文件。这些方法可能符合系统 ABI 的打包惯例。
打包与命名空间协同工作,以创建一种沙盒化形式。从软件包解析的组件将能够访问其命名空间中的打包内容。因此,组件作者可以将其他文件(例如本地化资源)与其组件打包在一起。反之,它们通常无法直接访问其他软件包中的文件。例如,系统字体是通过 FIDL 协议提供的,而不是作为直接文件访问。或者,开发者也可以打包自己的字体文件。
违规处置
如果可以规避在两个系统之间使用给定接口,那么这两个系统可能会变得更加紧密耦合,并且无法独立更改。Fuchsia 使用各种机制来强制执行平台接口的观察和遵循。
- 内核定义了一种 ABI,用于从用户模式代码进入内核,即系统调用 ABI。不过,该 ABI 是内核和 vDSO 之间的 ABI。应用必须调用 vDSO 导出的符号,而不是直接执行系统调用。由于内核和 vDSO 可能会一起进行版本控制,因此要求应用调用 vDSO 可让系统调用 ABI 无缝更改,而不会被应用开发者检测到。为了强制执行此属性,内核会在系统调用入口点检查调用方的地址,并确保该地址位于 vDSO 在调用方地址空间中的映射范围内。
- 内核提供了用于创建进程的系统调用。不过,程序加载的详细信息很复杂,因此这些信息被抽象化到进程启动器实现后面。为了确保应用开发者不会接触到这些详细信息,不允许组件直接创建进程,而是可以提供位于 FIDL 协议后面的进程启动器。这样,程序加载的详细信息便会随时间而变化。
- 任何文件系统目录都可以用作进程命名空间的根目录。这些命名空间可确保程序只能访问一组已知且已枚举的文件,从而防止它们形成意外的 ABI。为确保目录路径充当不可避免的根目录,Fuchsia 文件系统不实现“.”。