基于组件的软件

组件是在 Fuchsia 中运行的软件的基本构建块。每个组件都是一个可组合的沙盒化模块,可通过 capability 与其他组件交互。这可以提高系统安全性,并在各个组件之间创建清晰的接口,使其更易于更新或替换。

在 Fuchsia 中,一切都是组件(几乎)。回想一下前面对 Zircon 的讨论,我们特意将内核的表面积缩小,而大多数核心服务都是在用户空间中实现的。这意味着在 Fuchsia 上运行的大多数软件都是使用组件框架实现的,包括:

  • 面向用户的应用
  • 设备驱动程序
  • 文件系统
  • 媒体编解码器
  • 网络堆栈

在内核之外,只有少数几个不使用组件框架的低级异常,例如引导加载程序和 userboot 进程。

组件管理器

组件框架的核心是组件管理器。它负责协调所有组件实例的执行,为它们提供其功能,并在组件之间建立中间连接。

组件可以显式启动(例如,通过网址),也可以通过对特定功能的请求隐式启动。组件管理器会执行必要的解析,以确定是启动新组件还是将请求路由到现有实例。为了实现这种路由,每个组件都必须声明它向系统提供的所有功能及其使用的所有功能。

组件管理器会解析每个组件的声明,以确定如何运行组件并提供必要的功能。组件通常通过组件软件包内的组件清单文件向系统声明。

下面是一个简单的组件清单示例,该清单通过一些额外的命令参数描述了 ELF 可执行文件:

program: {
    runner: "elf",
    binary: "bin/hello",
    args: [ "Hello", "World!" ],
},

请注意,运行时声明会告知组件管理器此组件需要 ELF 运行程序这是一个 capability 的示例!

组件功能

组件通过capabilities获取访问更广泛的系统各个部分的权限。每个组件都可以声明它们为系统提供的新功能,以及它们需要运行的其他组件(或框架)提供的功能。

如您所见,runner 是一个功能示例,用于声明组件使用的运行时。其他常见功能类型的示例包括 directory(用于访问文件系统资源)和 protocol(用于与其他组件通信)。

开发者使用组件清单来声明组件所需的功能类型。下面是一个请求两项功能的组件清单示例:对 example-data 目录的读取权限以及对 fuchsia.example.Foo FIDL 协议描述的服务的访问权限。

use: [
    {
        directory: "example-data",
        rights: [ "r*" ],
        path: "/example/data",
    },
    {
        protocol: "fuchsia.example.Foo",
    },
]

组件管理器使用功能声明用必要的目录句柄填充每个组件的命名空间。在此示例中,组件将在其命名空间中收到 /example/data/svc/fuchsia.example.Foo

组件组织

系统中的所有组件都组合成一个有 root 权限的组件实例树。这种树形结构管理着组件行为的几个重要方面。

说明组件实例的组织方式的树状图。这些父子关系控制着组件行为的几个方面。

树中的父级组件负责创建其他组件的实例作为其子级,并为它们提供必要的功能。同时,子组件可以向父组件公开功能。可以通过以下两种方式之一创建子组件:

  • 静态:父组件在其自己的组件声明中声明存在子项。
  • 动态:父项在运行时使用 fuchsia.component.Realm 协议将子项添加到组件集合中。

任何父组件及其所有子组件在树中形成一个组,称为“realm”。领域使父项能够控制哪些功能流入和流出其组件子树,从而创建功能边界。组件使用 expose 关键字决定是否将功能导出到其领域之外的功能:

expose: [
    {
        protocol: "fuchsia.example.Foo",
        from: "self",
    },
],

向大区公开 capability 后,父级与同一大区中的其他组件共享该 capability。使用 offer 关键字可完成此操作:

offer: [
    {
        protocol: "fuchsia.example.Foo",
        from: "self",
    },
],

这称为“功能路由”。组件管理器只能解析在同一领域内公开和提供的功能。

显示组件如何通过“功能路由”共享功能的示意图,“功能路由”描述了资源在特定领域内的可用方式。

练习:组件

在本练习中,您将探索组件实例树,并详细了解使用一些核心系统组件的功能路由的实际运作方式。

启动模拟器

如果您尚未运行实例,请启动模拟器:

ffx emu start --headless

启动完成后,模拟器会输出以下消息并返回:

Logging to "$HOME/.local/share/Fuchsia/ffx/emu/instances/fuchsia-emulator/emulator.log"
Waiting for Fuchsia to start (up to 60 seconds)........
Emulator is ready.

探索系统组件

打开另一个终端窗口,并使用 component list 命令转储系统的组件树:

ffx component list

您应该会看到类似于以下(截断)列表的输出:

/
/bootstrap
/bootstrap/archivist
/bootstrap/base_resolver
/bootstrap/console
/bootstrap/console-launcher
/bootstrap/decompressor
/bootstrap/device_name_provider
/bootstrap/driver_manager
/bootstrap/fshost
/bootstrap/kernel_debug_broker
/bootstrap/miscsvc
/bootstrap/netsvc
/bootstrap/power_manager
/bootstrap/ptysvc
/bootstrap/pwrbtn-monitor
/bootstrap/shutdown_shim
/bootstrap/sysinfo
/bootstrap/virtual_console
/core
/core/activity
...
/core/detect
/core/font_provider
/core/log-stats
/core/remote-control
/core/sampler
/core/system-update-committer
/core/temperature-logger
/core/test_manager
/core/full-resolver
/startup

此列表表示组件实例树,其根下方有 bootstrapcorestartup 等组织组件。

component show 命令提供有关每个组件的更多详细信息。

使用以下命令查看 http-client(提供 HTTP 请求服务的组件)的详细信息:

ffx component show http-client.cm

该命令会输出以下报告:

               Moniker: /core/network/http-client
                   URL: #meta/http-client.cm
                  Type: CML static component
       Component State: Resolved
 Incoming Capabilities: config
                        fuchsia.logger.LogSink
                        fuchsia.net.name.Lookup
                        fuchsia.posix.socket.Provider
                        pkg
  Exposed Capabilities: fuchsia.net.http.Loader
           Merkle root: d9e73f5b061f2f227e596e2e0079ff3a095fc69e192cf85e0d7621826c76356c
       Execution State: Running
          Start reason: '/core/feedback' requested capability 'fuchsia.net.http.Loader'
         Running since: ...
                Job ID: 41268
            Process ID: 41311
 Outgoing Capabilities: fuchsia.net.http.Loader

请注意此处报告的一些详细信息:

  1. 组件实例的唯一标识符(称为名称)。
  2. 加载此组件的软件包网址。
  3. 组件的执行状态。
  4. 运行实例的当前作业/进程 ID。
  5. 为组件请求的一组公开的功能。

跟踪功能路由

在前面的输出中,列出了三个功能组:

  • 传入功能:组件使用 use 声明的功能。这些信息通过组件的命名空间提供给组件。
  • 传出功能:组件已发布到其传出目录的功能。
  • 公开的功能:组件使用 expose 声明的功能。这些是组件提供的服务

http-client 向其父级 realm 提供的其中一项功能是 fuchsia.net.http.Loader。这样一来,其他组件便可发出 HTTP 请求。

使用 component select 命令确定有多少组件与此功能交互:

ffx component capability fuchsia.net.http.Loader

该命令会列出所有匹配的组件:

Exposed:
  /core/network/http-client
  /core/network
Used:
  /core/cobalt
  /core/feedback
  /core

这表示 cobaltfeedback 组件使用此功能(即,将其列在传入功能下)。这些组件之间的共同祖先实体是 core,它负责将此功能路由到必要的子项。