Zircon 的 C++

Zircon 树中使用了 C++17 语言的子集。这包括 内核和用户空间代码。C++ 混合使用 C 语言(以及一些汇编语言) 两个地方。应避免使用或禁止使用某些 C++ 语言功能。使用 C++ 标准库功能非常谨慎。

语言功能

  • 不允许 <ph type="x-smartling-placeholder">
      </ph>
    • 异常
    • RTTI 和 dynamic_cast
    • 运算符过载
    • 虚拟继承
    • 静态构建的对象
    • 尾随返回值类型语法 <ph type="x-smartling-placeholder">
        </ph>
      • 例外情况:当需要对具有其他无法明确返回值类型的 lambda 执行必要操作时
    • 初始化程序列表
    • 内核代码中的 thread_local
  • 允许 <ph type="x-smartling-placeholder">
      </ph>
    • 纯接口继承
    • lambda
    • constexpr
    • nullptr
    • enum classes
    • template
    • 默认参数 <ph type="x-smartling-placeholder">
        </ph>
      • 但要运用判断力。末尾的一个可选输出参数是 可能没什么问题。四个可选的 bool 参数,很可能不是。
    • 普通的老式类
    • auto
    • 多重实现继承 <ph type="x-smartling-placeholder">
        </ph>
      • 但要保持谨慎。这广泛用于侵扰性 容器 mixins。
  • 需要更多规定 TODO(cpu) <ph type="x-smartling-placeholder">
      </ph>
    • 全局构造函数 <ph type="x-smartling-placeholder">
        </ph>
      • 目前,我们针对全局数据结构提供了这些方法。

TODO: 指向风格指南的指针?

C++ 标准版

Zircon 代码使用 -std=c++17 构建,通常可以使用 C++ 17 语言 和库功能(具体取决于所描述的样式/功能限制) 请参阅上文和图书馆使用指南 下文)。您可以一般 与 C++ 14 或更低版本兼容。当标准 C++ 17 功能 以最干净的方式做某件事,那就去做吧。

所有纯 C 代码(.c 源文件和它们使用的头文件)都是 C 11。部分 对于要通过树外启动重复使用的代码设有特殊例外情况 加载程序(针对嵌入式代码坚持使用保守的 C 89 子集)。

标准库

C++ 标准库 API 具有许多变化广泛的接口 特征。我们将标准库 API 细分为 基于每个类别的可预测性和复杂性,确定以下类别 特定界面的代码生成以及对机器和操作系统设施的使用。 这些圆可看作是 最小的类似 C 语言子集扩展到完整 C++ 17 API。

背景信息至关重要

本部分提供了一些指导,帮助您了解使用 特定的标准 C++ 库 API。没有任何 硬性规则和快速规则除外,但内核规则(请参见下一部分)除外, 实施限制,大家始终希望该限制是临时的。

谨慎行事是根本。

  • 考虑您对时间和空间的复杂性、动态 分配行为(如果有)以及您使用的每个 API 的故障模式。

  • 然后考虑使用它的具体情境,以及使用方式 确保背景信息与各种疑虑密切相关。

  • 尤其要注意依赖于输入的行为,因为这些行为可能会快速 使用重要的库工具时,预测难度会大大增加。

如果您在驱动程序中编写主要 I/O 逻辑, 延迟时间、吞吐量或可靠性 那么您在所依赖的图书馆设施方面应该比较保守 从技术层面来讲,用户空间中提供了所有这些事件(不过, 内核;(请参阅下一部分)。但您并不需要太多 。您可能不想依赖大量会执行相关任务的 std 容器, 动态的动态分配机制否则会导致 理解、预测和控制存储空间/内存占用量、分配 服务的行为、性能和可靠性。

尽管如此,即便是驱动程序也是一个用户空间程序, 配置文件或参数等对于所有那些非必要或 非热路径的一部分的启动函数,使用更复杂的 那么库设施很可能就没什么问题了。只是 请务必注意代码的总体指标,例如 运行时内存使用量最少/总/峰值、代码膨胀(同时使用两种设备) 存储和运行时内存)以及意外故障模式的弹性。 也许不要只是为了 利用以下酷炫的配置解析库

内核中没有 std

C++ std 命名空间不能用在内核代码中, 还包含引导加载程序。为数不多的 C++ 标准库 不涉及 std:: API 的头文件仍可直接使用。请参阅 。

不应在内核代码中使用其他 C++ 标准头文件。相反, 内核中值得拥有的任何库工具(例如 std::move)通过特定于内核的 API(例如 ktl::move)。这些 API 的内核实现实际上可能 依赖于工具链标头提供 std:: 实现, 别名为内核 API 名称。但只有这些 API 实现和 在某些库头文件中,非常特殊的情况应使用 std::

通用标头

这些标头 API 可在任何位置安全使用,即使在内核中也是如此。

它们在标准 C 接口的子集上包含 C++ 封装容器, 内核支持:

不应包含这些头文件中的 C 库 API 的 std 命名空间别名 可在内核代码中使用

即使在内核中,也可以使用一个纯 C++ 头文件:

保守的用户空间

这些标头 API 可在任何位置安全使用。不允许在 内核,因为它们全部位于 std 命名空间中。但 如果存在以下情况,这些 API 很有可能获得内核中的 API 别名: 在内核代码中使用此类 API 的理想情况。

这些是纯标头类型和模板。它们不会进行任何动态变化 资源分配每个函数的时间和空间复杂度 应该从说明中清晰明确

这涉及一些动态分配,但仅限于显式分配:

  • <any>
  • <memory>

    std::shared_ptrstd::weak_ptrstd::auto_ptr API 应 绝不使用。请改用 std::unique_ptrfbl::RefPtr

仅限用户空间

这些都是通过任何类似的 API 或根本无法实现的 或名称但它们通常在 用户空间。它们不涉及动态分配。

厨房水槽

这涉及到难以预测的动态分配, 。确切的运行时行为和内存要求 很难推断。在任何应用中使用这些界面之前都要三思而后行 提升可靠性或性能的关键路径, 让它更精简、节省空间

FBL

FBL 是 Fuchsia 基础库,在内核和用户空间之间共享。 因此,FBL 具有非常严格的依赖关系。例如,FBL 不能依赖 ,因为系统调用接口在 由内核执行同样,如果 C 库功能不依赖于 FBL, 内核版本。

  1. system/ulib/fbl,可通过 内核和用户空间
  2. kernel/lib/fbl:只能通过 由内核执行

FBL 提供:

FBL 严格控制内存分配。内存分配应 明确指定,使用 AllocChecker 让客户端从分配中恢复 错误。在某些情况下,允许隐式内存分配,但 隐式分配内存的函数必须 #ifdef'ed 不可用 内核中。

FBL 在平台源代码树之外不可用。

ZX

ZX 包含适用于 Zircon 对象系统调用。这些封装容器提供类型安全和移动语义 ,但对于 syscalls.abigen 中的内容,不提供任何意见。在特定 我们可能会从 syscalls.abigen 自动生成 ZX,类似于 如何自动生成其他语言的系统调用封装容器。

ZX 是 Fuchsia SDK 的一部分。

FZL

FZL 是 Fuchsia Zircon 图书馆。此库可为常见的使用场景 涉及内核对象的操作,并且有权就如何 与 Zircon 系统调用交互。如果一段代码不依赖于 Zircon 系统调用,代码应改为存储在 FBL 中。

FZL 在平台源代码树之外不可用。

封闭 C++

我们建议自始至终都使用 C++ 而不是 C 作为实现语言 紫红色。但在许多情况下,我们需要较窄的 ABI 瓶颈 简化了防止、跟踪或适应 ABI 偏移的问题。通过 使 ABI 保持简单的第一个关键方法是基于纯 C API( 可直接通过 C++ 使用,也可通过许多其他 语言),而不是 C++ API。当我们将一段代码连接到 模块中使用纯 C 外部 API 和 ABI,但内部使用 C++ 进行其 我们将其称为“封闭 C++”。

  • 可以说,内核本身是用封闭的 C++ 实现的。
  • vDSO 是使用封闭 C++ 实现的共享库。
  • Fuchsia 的标准 C 库,虽然在很大程度上实现了 也在其实现中使用了封闭的 C++。
  • 大多数 Fuchsia 设备驱动程序都是使用封闭的 C++ 实现的。

对于通过 Fuchsia 的公共 SDK 导出的二进制文件,有一条硬性规定 共享库必须具有纯 C API 和 ABI。此类库 并且应该在其实现中使用 C++ 而非 C,并且他们可以使用 包含 C++ API 的其他静态链接库(只要其 ABI 方面) 这些内部 C++ API 不会泄露到共享库的公共 ABI 中。

“可加载模块”(有时称为“插件”模块)非常类似于 共享库。关于纯 C ABI 瓶颈的相同规则同样适用于 可加载模块 ABI。Fuchsia 设备驱动程序就是这样一种可加载的模块 必须满足驱动程序(纯 C)ABI 的要求。因此,在 Google Cloud 中 C++ 必须使用封闭的 C++。

Fuchsia C++ 工具链使用 libc++ 实现。在 C++ 可执行文件(以及 具有 C++ ABI 的共享库)(通常是动态链接),以及 这是编译器的默认行为。工具链还提供了 libc++,用于通过 -static-libstdc++ 开关切换到封闭静态链接 编译器 (clang++)。在 Zircon GN 构建系统中,链接目标 例如 executable()test()library()(使用 shared = true),使用 以下代码行来请求封闭的 C++ 标准库:

    configs += [ "//zircon/public/gn/config:static-libc++" ]

在导出到公共 IDK 的每个 library() 中,您都必须执行此操作 以二进制形式通过 sdk = "shared" 传递的。

每个 driver() 都会自动使用封闭的 C++,因此这行代码 所需的资源(驱动程序不能依赖于自己的共享库,只能依赖于 驱动程序 ABI 提供的动态链接环境。)

对于可执行文件和未导出的共享库,我们要做的是 针对标准 C++ 库使用静态链接还是动态链接。 在 Fuchsia 的软件包部署模型中,没有特殊的可更新性 与许多其他系统相比,共享库使用方面有了改进。主 权衡是可以通过大量存储数据节省内存和 使用完全相同的共享资源和进程 库二进制文件和紧凑性,以及(有时是性能)的 软件包。由于系统 build 中的许多软件包将全部使用相同的共享 libc++ 库,除非有 特殊情况它是编译器和构建系统中的默认设置。