基于组件的软件

组件是在 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。Realms 让父项能够控制哪些功能流入和流出其组件子树,从而创建功能边界。组件使用 expose 关键字决定是否将功能导出到其领域之外:

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

将某项 capability 提供给大区后,其父项便可与同一大区中的其他组件共享该 capability。使用 offer 关键字可完成此操作:

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

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

展示组件如何通过“功能路由”共享功能的示意图,“功能路由”描述了如何在特定领域提供资源。

练习:组件

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

启动模拟器

如果您尚未运行实例,请启动提供网络支持的 FEMU:

ffx emu start workstation_eng.x64 --headless

探索系统组件

打开另一个终端窗口,然后使用 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 capability 命令确定有多少组件与此功能交互:

ffx component capability fuchsia.net.http.Loader

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

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

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