連結元件

本文件說明如何使用功能將元件串連在一起 和上層元件適用的其他工具

概念

繼續閱讀本指南之前,請先瞭解以下概念:

* 元件架構結合了 命名空間 的 建構元件 元件宣告 並 描述 功能 為此元件 函式。元件公開給他人的功能會組合成 換 公開的目錄 ,直接在 Google Cloud 控制台實際操作。 * 每個元件都會收到 Directory 管道,稱為 傳出目錄 ,直接在 Google Cloud 控制台實際操作。元件的程式 能夠發掘透過此目錄提供的所有功能。 * 在執行階段, 元件執行個體樹狀結構 描述了父項和子項關係 元件執行個體 ,直接在 Google Cloud 控制台實際操作。元件執行個體 樹狀結構,以及該樹狀結構上的能力路徑 作為 元件拓撲 ,直接在 Google Cloud 控制台實際操作。 * 父項元件會在 元件資訊清單 或是使用 元件集合 ,直接在 Google Cloud 控制台實際操作。集合是一種 動態子項容器,可能會在執行階段建立及刪除 使用 fuchsia.component.Realm 架構通訊協定

如要進一步瞭解這些概念,請參閱運作範圍,以及 功能

透過轉送連接功能

元件會透過功能彼此互動。服務依據 在該元件的資訊清單中,您需要宣告一個元件 可供他人使用,並透過 exposeoffer 轉送至父項/子項元件 宣告的內容其他使用該能力的元件也需要宣告 並運用在資訊清單中為了讓程式能 必須使用的能力必須轉送至該元件 或由孩子公開提供的資源

功能轉送是指由元件執行的遞迴程序 管理者,可依循個別轉送方式來識別服務元件 所描述的步驟,如資訊清單檔案中所述。在下列情況下,即會開始轉送能力:

  • 元件程式會在其程式開啟路徑 命名空間 ,直接在 Google Cloud 控制台實際操作。
  • 元件的程式會在另一個元件的 公開的目錄 ,直接在 Google Cloud 控制台實際操作。
  • 開發人員叫用 ffx component route
  • 啟動需要依附於支援器或執行器的元件 解析器或執行元件功能尚未轉送。

轉送作業延後執行:雖然能力可能設定為 由家長或下層提供 (直接或間接透過 時,目標元件可能尚未解決 已啟動轉送作業。這代表完整的路線 傳送至最終供應元件的請求元件 嘗試轉送。

提供能力實作

提供能力的元件必須在其 透過 capabilities 宣告來元件資訊清單。

請參閱宣告 FIDL 的範例 通訊協定元件

{
    include: [
        "inspect/client.shard.cml",
        "syslog/client.shard.cml",
    ],

    // Information about the program to run.
    program: {
        // Use the built-in ELF runner.
        runner: "elf",

        // The binary to run for this component.
        binary: "bin/echo_server_rust",
    },

    // Capabilities provided by this component.
    capabilities: [
        { protocol: "fidl.examples.routing.echo.Echo" },
    ],
    expose: [
        {
            protocol: "fidl.examples.routing.echo.Echo",
            from: "self",
        },
    ],
}

在執行階段,伺服器元件會為 使用 fuchsia.io 在傳出目錄中提供該物件 因此效能相當卓越產生的 FIDL 繫結會包裝這個帳號代碼並啟用供應器 開始接收傳入的要求:

荒漠油廠

// Wrap protocol requests being served.
enum IncomingRequest {
    Echo(EchoRequestStream),
}

#[fuchsia::main(logging = false)]
async fn main() -> Result<(), anyhow::Error> {
    let mut service_fs = ServiceFs::new_local();

    // Initialize inspect
    component::health().set_starting_up();
    let _inspect_server_task = inspect_runtime::publish(
        component::inspector(),
        inspect_runtime::PublishOptions::default(),
    );

    // Serve the Echo protocol
    service_fs.dir("svc").add_fidl_service(IncomingRequest::Echo);
    service_fs.take_and_serve_directory_handle().context("failed to serve outgoing namespace")?;

    // Component is serving and ready to handle incoming requests
    component::health().set_ok();

    // Attach request handler for incoming requests
    service_fs
        .for_each_concurrent(None, |request: IncomingRequest| async move {
            match request {
                IncomingRequest::Echo(stream) => handle_echo_request(stream).await,
            }
        })
        .await;

    Ok(())
}

// Handler for incoming service requests
async fn handle_echo_request(mut stream: EchoRequestStream) {
    while let Some(event) = stream.try_next().await.expect("failed to serve echo service") {
        let EchoRequest::EchoString { value, responder } = event;
        responder.send(value.as_ref().map(|s| &**s)).expect("failed to send echo response");
    }
}

C++

```cpp // 傳入服務要求的處理常式 class EchoImplementation : public fidl::examples::routing::echo::Echo { 公開: void EchoString(fidl::StringPtr value, EchoStringCallback 回呼) 覆寫 { call(value);} fidl::examples::routing::echo::Echo_EventSender* event_sender_; };

int main(int argc, const char** argv) { async::Loop loop(&amp;kAsyncLoopConfigAttachToCurrentThread); 自動內容 = sys::ComponentContext::CreateAndServeOutgoingDirectory(); // 初始化檢查 inspect::ComponentInspector inspector(loop.dispatcher(), inspect::PublishOptions{}); inspector.Health().StartingUp(); // 提供 Echo 通訊協定 Echo 實作 echo_instance; fidl::Binding&lt;fidl::examples::routing::echo::Echo&gt;binding(&amp;echo_instance); echo_instance.event_sender_ = &binding.events(); fidl::InterfaceRequestHandler&lt;fidl::examples::routing::echo::Echo&gt;處理常式 = [&amp;](fidl::InterfaceRequest&lt;fidl::examples::routing::echo::Echo&gt; request) { binding.Bind(std::move(request)); }; context-&gt;outgoing()-&gt;AddPublicService(std::move(handler)); // 元件正在提供服務,並準備好處理傳入的要求 inspector.Health().Ok(); returnLoop.Run(); } ```

連線至轉送功能

用戶端元件會在元件中宣告可能要求的功能 透過 use 宣告的資訊清單。

請參考以下使用 FIDL 的用戶端元件資訊清單範例 透過先前元件提供的通訊協定

{
    include: [
        // Enable logging on stdout
        "syslog/client.shard.cml",
    ],

    // Information about the program to run.
    program: {
        // Use the built-in ELF runner.
        runner: "elf",

        // The binary to run for this component.
        binary: "bin/echo_client_rust",

        // Program arguments
        args: [ "Hello Fuchsia!" ],
    },


    // Capabilities used by this component.
    use: [
        { protocol: "fidl.examples.routing.echo.Echo" },
    ],
}

在執行階段,用戶端元件會使用 fuchsia.io 通訊協定以取得功能 其他元件提供的元件Fuchsia 元件庫可與 所產生的 FIDL 繫結,提供結構化介面 管道:

荒漠油廠

#[fuchsia::main]
async fn main() -> Result<(), anyhow::Error> {
    // Parse arguments, removing binary name
    let mut args: Vec<String> = std::env::args().collect();
    args.remove(0);

    // Connect to FIDL protocol
    let echo = connect_to_protocol::<EchoMarker>().expect("error connecting to echo");

    // Send messages over FIDL interface
    for message in args {
        let out = echo.echo_string(Some(&message)).await.expect("echo_string failed");
        tracing::info!("Server response: {}", out.as_ref().expect("echo_string got empty result"));
    }

    Ok(())
}

C++

int main(int argc, const char* argv[], char* envp[]) {
  // Set tags for logging.
  fuchsia_logging::LogSettingsBuilder builder;
  builder.WithTags({"echo_client"}).BuildAndInitialize();

  // Connect to FIDL protocol
  fidl::examples::routing::echo::EchoSyncPtr echo_proxy;
  auto context = sys::ComponentContext::Create();
  context->svc()->Connect(echo_proxy.NewRequest());

  // Send messages over FIDL interface for each argument
  fidl::StringPtr response = nullptr;
  for (int i = 1; i < argc; i++) {
    ZX_ASSERT(echo_proxy->EchoString(argv[i], &response) == ZX_OK);
    if (!response.has_value()) {
      FX_LOG_KV(INFO, "echo_string got empty result");
    } else {
      FX_LOG_KV(INFO, "Server response", FX_KV("response", response->c_str()));
    }
  }

  return 0;
}

元件的父項必須負責將元件轉送至該元件 即便沒有技術背景,也能因這些工具的功能而受益

將部分功能標示為非必要功能

元件使用的所有功能不一定都能運作 如果元件 而實作能力只會啟用 替代行為

使元件架構瞭解元件功能 需要,以及元件是選用哪些功能,請使用 「availability」欄位。

use: [
    {
        // It is ok if this protocol is unavailable
        protocol: "fuchsia.examples.Echo1",
        availability: "optional",
    },
    {
        // This protocol MUST be provided for the component to function correctly.
        protocol: "fuchsia.examples.Echo2",
        availability: "required",
    },
]

如果元件具有針對能力有 required 使用宣告 (預設值) 但其父項提供的能力為 optional,那麼 「能力工具」就會產生錯誤 一律會失敗。

使用選用功能

在元件的父項中,offer 設有功能 availability: "optional",該能力可能無法在執行階段使用。

元件中的項目 命名空間 會顯示 以及此能力是否可用任何嘗試開放 該能力會產生提供給 Directory.Open() 的控制代碼 用 ZX_ERR_NOT_FOUND 符號打烊的電話。

使用 libc 方法 (例如 open()stat()) 會傳回 ENOENT

路徑功能

元件只能存取轉送到其中的功能。能力 源自元件拓撲中的任何位置,只要具備有效的能力 路徑存在為來自能力中下列宣告的鏈結 提供給任何消費者:

  • expose:將能力轉送至元件的父項。
  • offer:將能力向下轉送至元件的其中一個元件 。

為了將能力供應商與要求這些功能的元件連結, :

  1. offerexpose 宣告新增至能力供應器元件:

    {
        include: [
            "inspect/client.shard.cml",
            "syslog/client.shard.cml",
        ],
    
        // Information about the program to run.
        program: {
            // Use the built-in ELF runner.
            runner: "elf",
    
            // The binary to run for this component.
            binary: "bin/echo_server_rust",
        },
    
        // Capabilities provided by this component.
        capabilities: [
            { protocol: "fidl.examples.routing.echo.Echo" },
        ],
        expose: [
            {
                protocol: "fidl.examples.routing.echo.Echo",
                from: "self",
            },
        ],
    }
    
  2. 對於元件執行個體樹狀結構中的每個中繼元件,請加入 額外的 exposeoffer 宣告,直到觸及 包含 use 宣告的元件:

    {
        // Two children: a server and client.
        children: [
            {
                name: "echo_server",
                url: "#meta/echo_server.cm",
            },
            {
                name: "echo_client",
                url: "#meta/echo_client.cm",
            },
        ],
        offer: [
            // Route Echo protocol from server to client.
            {
                protocol: "fidl.examples.routing.echo.Echo",
                from: "#echo_server",
                to: "#echo_client",
            },
    
            // Route diagnostics protocols to both children.
            {
                protocol: [
                    "fuchsia.inspect.InspectSink",
                    "fuchsia.logger.LogSink",
                ],
                from: "parent",
                to: [
                    "#echo_client",
                    "#echo_server",
                ],
            },
        ],
    }
    

選用的依附元件

如果元件對能力有選用依附元件,則該元件會 該元件的父項來決定該元件是否會接收 不一定。提供能力時,元件可能會設定 availability 欄位 轉換為 optionalrequiredsame_as_target。每個值都有 下列語意:

  • optional:優惠目標必須宣告自身能處理 如果缺少此能力,請將其的 use 宣告標示為 optional。 如果目標無法這麼做 (例如,目標的可用性為 required 用於這項能力),則轉送能力會造成 錯誤。
  • required:目標必須接收這項能力。如果優惠來源是 parent 和元件的父項 (目標的祖系) 都提供了這個項目 反之,而轉送該能力會導致錯誤 因為父項無法保證能力的可用性。
  • same_as_target:這項能力是否可用取決於 達到預期的目標如果目標有選用依附元件 能力,這項方案也會是選用項目如果目標為 這項能力所需的依附元件 這通常代表交易 不會十分要求關聯語意
offer: [
    {
        // child-1 MUST receive the protocol 'fuchsia.logger.LogSink'.
        protocol: "fuchsia.logger.LogSink",
        to: "#child-1",
        from: "#child-2",
        availability: "required",
    },
    {
        // child-1 MUST be able to handle the absence of the protocol
        // 'fuchsia.tracing.provider.Registry'.
        protocol: "fuchsia.tracing.provider.Registry",
        to: "#child-1",
        from: "parent",
        availability: "optional",
    },
    {
        // child-1 decides if it must receive the protocol
        // 'fuchsia.posix.socket.Provider', or if it must be able to support its
        // absence.
        protocol: "fuchsia.posix.socket.Provider",
        to: "#child-1",
        from: "parent",
        availability: "same_as_target",
    },
]

與使用宣告相同,您可以省略 availability 欄位, 在這個情況下,預設值為 required

轉換型依附元件

元件架構讓元件可在 BERT 環境中 並提供選用與必要功能在 2017 年 可用性標記是使用能力的元件 父項為必要、選用或與驗證錯誤 目標。請注意,雖然有這個欄位是用來啟用軟轉換,但元件 最後應選項為選用或必要項目

如要使用這項功能,子項元件會將其可用性標示為 "過渡期":

use: [
    {
        // It is ok if this protocol is offered as "required" or "optional"
        protocol: "fuchsia.examples.Echo",
        availability: "transitional",
    },
]

管理子項元件

在元件拓撲中的任何位置,元件都可互相互動 但前提是兩者之間必須有一個有效的能力路徑。 還有其他方法可讓父項元件與 這類行為

下列元件範例宣告了一個名為 lifecycle 的單一靜態子項 以及名為 echo 的集合,可用於建立其他子元件 的廣告:

{
    // ...

    // Statically declared child components
    children: [
        {
            name: "lifecycle",
            url: "lifecycle#meta/default.cm",
        },
    ],

    // Collection to hold dynamically created child components
    collections: [
        {
            name: "echo",
            durability: "transient",
        },
    ],

    // Capabilities required by this component
    use: [
        {
            protocol: "fuchsia.component.Binder",
            from: "#lifecycle",
        },
        {
            protocol: "fuchsia.component.Realm",
            from: "framework",
        },
    ],

    // Capabilities required by child components
    offer: [
        {
            protocol: [
                "fuchsia.inspect.InspectSink",
                "fuchsia.logger.LogSink",
            ],
            from: "parent",
            to: [
                "#echo",
                "#lifecycle",
            ],
        },
    ],
}

請注意,集合的運作方式類似於父項中的靜態子項執行個體 設定元件的資訊清單,您可以為元件命名並提供特定功能, 基礎架構集合中的所有子元件可存取一組功能 沒有任何影響

啟動子項元件

元件架構提供 fuchsia.component.Binder 適用於父項元件的通訊協定,明確啟動可能不公開的子項 任何其他功能由於這項能力是由架構提供, 子項元件只需要從元件資訊清單公開:

{
    // ...

    // Capabilities exposed from this component to parent.
    expose: [
        {
            // Expose this protocol so that parent component can start it
            // by binding to this capability.
            protocol: "fuchsia.component.Binder",
            from: "framework",
        },
    ],
}

建立動態子項

如要在執行階段建立新的子項元件,請使用 使用 fuchsia.component.Realm 通訊協定在內部建立元件 現有的集合呼叫 CreateChild 方法,包含以下參數:

  • CollectionRef:說明集合的位置 即可新增元件
  • Child:元件宣告,包括其名稱和 元件網址

荒漠油廠

use fidl_fuchsia_component::{BinderMarker, CreateChildArgs, RealmMarker};
use fidl_fuchsia_component_decl::{Child, ChildRef, CollectionRef, StartupMode};

// ...

// Use the fuchsia.component.Realm protocol to create a dynamic
// child instance in the collection.
async fn create_dynamic_child() {
    let realm = client::connect_to_protocol::<RealmMarker>()
        .expect("failed to connect to fuchsia.component.Realm");

    let collection_ref = CollectionRef { name: String::from("echo") };
    let child_decl = Child {
        name: Some(String::from("lifecycle_dynamic")),
        url: Some(String::from("echo_server#meta/default.cm")),
        startup: Some(StartupMode::Lazy),
        ..Default::default()
    };

    realm
        .create_child(&collection_ref, &child_decl, CreateChildArgs::default())
        .await
        .expect("create_child failed")
        .expect("failed to create child");
}

C++

#include <fuchsia/component/cpp/fidl.h>
#include <fuchsia/component/decl/cpp/fidl.h>

// ...

// Use the fuchsia.component.Realm protocol to create a dynamic
// child instance in the collection.
void CreateDynamicChild() {
  fuchsia::component::decl::CollectionRef collection_ref = {
      .name = "echo",
  };
  fuchsia::component::decl::Child child_decl;
  child_decl.set_name("lifecycle_dynamic");
  child_decl.set_url("echo_server#meta/default.cm");
  child_decl.set_startup(fuchsia::component::decl::StartupMode::LAZY);

  realm_proxy_->CreateChild(std::move(collection_ref), std::move(child_decl),
                            fuchsia::component::CreateChildArgs(),
                            [&](fuchsia::component::Realm_CreateChild_Result result) {
                              ZX_ASSERT(!result.is_err());
                              FX_LOGS(INFO) << "Dynamic child instance created.";

                              ConnectDynamicChild();
                            });
}

與孩子交流互動

由於在建構期間無法得知動態元件的父項,因此其 加強版能力路徑中,不得以 元件資訊清單。

如要連線至動態子項執行個體所公開的功能:

  1. 使用 fuchsia.component.Realm 通訊協定開啟子項的 公開目錄呼叫 OpenExposedDir 方法,其中包含子項元件的名稱和集合名稱:

    荒漠油廠

    use fidl_fuchsia_component::{BinderMarker, CreateChildArgs, RealmMarker};
    use fidl_fuchsia_component_decl::{Child, ChildRef, CollectionRef, StartupMode};
    
    // ...
    // Connect to the fidl.examples.routing.echo capability exposed by the child
    // instance, and send a request.
    async fn connect_dynamic_child(message: String) {
        // Open the child's exposed directory
        let exposed_dir = client::open_childs_exposed_directory(
            String::from("lifecycle_dynamic"),
            Some(String::from("echo")),
        )
        .await
        .expect("failed to open exposed directory");
    
        // ...
    }
    

    C++

    #include <fuchsia/component/cpp/fidl.h>
    #include <fuchsia/component/decl/cpp/fidl.h>
    
    // ...
    
    // Use the fuchsia.component.Realm protocol to open the exposed directory of
    // the dynamic child instance.
    void ConnectDynamicChild() {
      fuchsia::component::decl::ChildRef child_ref = {
          .name = "lifecycle_dynamic",
          .collection = "echo",
      };
    
      fidl::InterfaceHandle<fuchsia::io::Directory> exposed_dir;
      realm_proxy_->OpenExposedDir(
          child_ref, exposed_dir.NewRequest(),
          [this, exposed_dir = std::move(exposed_dir)](
              fuchsia::component::Realm_OpenExposedDir_Result result) mutable {
            ZX_ASSERT(!result.is_err());
            std::shared_ptr<sys::ServiceDirectory> svc = std::make_shared<sys::ServiceDirectory>(
                sys::ServiceDirectory(std::move(exposed_dir)));
    
            SendEchoRequest(svc);
          });
    }
    
  2. 使用公開目錄連線至子項公開的功能 作為根:

    荒漠油廠

    // Access the fidl.examples.routing.echo capability provided there
    let echo = client::connect_to_protocol_at_dir_root::<EchoMarker>(&exposed_dir)
        .expect("failed to connect to fidl.examples.routing.echo");
    
    let response = echo
        .echo_string(Some(&message))
        .await
        .expect("echo_string failed")
        .expect("echo_string got empty result");
    info!("Server response: {}", response);
    

    C++

    // Connect to the fidl.examples.routing.echo capability exposed by the child's
    // service directory.
    void SendEchoRequest(std::shared_ptr<sys::ServiceDirectory> svc_directory) {
      // Connect to the protocol inside the child's exposed directory
      svc_directory->Connect(echo_proxy_.NewRequest());
    
      // Send a protocol request
      echo_proxy_->EchoString(message_, [&](fidl::StringPtr response) {
        FX_LOGS(INFO) << "Server response: " << response;
        DestroyDynamicChild();
      });
    }
    

破壞動態子項

不再需要動態子項時,請使用 fuchsia.component.Realm 通訊協定來刪除元件 執行個體。使用下列指令呼叫 DestroyChild 方法: ChildRef,代表集合中的子項。

荒漠油廠

use fidl_fuchsia_component::{BinderMarker, CreateChildArgs, RealmMarker};
use fidl_fuchsia_component_decl::{Child, ChildRef, CollectionRef, StartupMode};

// ...

// Use the fuchsia.component.Realm protocol to destroy the dynamic
// child instance running in the collection.
async fn destroy_dynamic_child() {
    let realm = client::connect_to_protocol::<RealmMarker>()
        .expect("failed to connect to fuchsia.component.Realm");

    let child_ref = ChildRef {
        name: String::from("lifecycle_dynamic"),
        collection: Some(String::from("echo")),
    };

    realm
        .destroy_child(&child_ref)
        .await
        .expect("destroy_child failed")
        .expect("failed to destroy child");
}

C++

#include <fuchsia/component/cpp/fidl.h>
#include <fuchsia/component/decl/cpp/fidl.h>

// ...

// Use the fuchsia.component.Realm protocol to destroy the dynamic
// child instance running in the collection.
void DestroyDynamicChild() {
  fuchsia::component::decl::ChildRef child_ref = {
      .name = "lifecycle_dynamic",
      .collection = "echo",
  };

  realm_proxy_->DestroyChild(std::move(child_ref),
                             [&](fuchsia::component::Realm_DestroyChild_Result result) {
                               ZX_ASSERT(!result.is_err());
                               FX_LOGS(INFO) << "Dynamic child instance destroyed.";

                               // Terminate the loop
                               loop_->Quit();
                             });
}

這會讓元件在目前正在執行時停止運作。為了處理這種情況 事件中,請參閱監聽停止事件元件。

控制元件生命週期

元件架構提供修改項目以及與各種項目互動的功能 元件部分

如要進一步瞭解生命週期概念,請參閱 元件生命週期

生命週期通知

ELF 執行元件會使用 fuchsia.process.lifecycle.Lifecycle 通訊協定。

如要監聽子元件中的停止通知:

  1. 訂閱元件中的生命週期事件 manifest:

    荒漠油廠

    // Information about the program to run.
    program: {
        // Use the built-in ELF runner.
        runner: "elf",
    
        // The binary to run for this component.
        binary: "bin/lifecycle_example_rust",
    
        // Subscribe to component lifecycle events
        lifecycle: { stop_event: "notify" },
    },
    

    C++

    // Information about the program to run.
    program: {
        // Use the built-in ELF runner.
        runner: "elf",
    
        // The binary to run for this component.
        binary: "bin/lifecycle_example_cpp",
    
        // Subscribe to component lifecycle events
        lifecycle: { stop_event: "notify" },
    },
    
  2. 使用 執行元件:

    荒漠油廠

    use fidl_fuchsia_process_lifecycle::{LifecycleRequest, LifecycleRequestStream};
    
    // ...
    
    #[fuchsia::main(logging_tags = ["lifecycle", "example"])]
    async fn main() {
        // Take the lifecycle handle provided by the runner
        match fuchsia_runtime::take_startup_handle(HandleInfo::new(HandleType::Lifecycle, 0)) {
            Some(lifecycle_handle) => {
                info!("Lifecycle channel received.");
                // Begin listening for lifecycle requests on this channel
                let x: fuchsia_zircon::Channel = lifecycle_handle.into();
                let async_x = AsyncChannel::from(fuchsia_async::Channel::from_channel(x));
                let mut req_stream = LifecycleRequestStream::from_channel(async_x);
                info!("Awaiting request to close...");
                if let Some(request) =
                    req_stream.try_next().await.expect("Failure receiving lifecycle FIDL message")
                {
                    match request {
                        LifecycleRequest::Stop { control_handle: c } => {
                            info!("Received request to stop. Shutting down.");
                            c.shutdown();
                            process::exit(0);
                        }
                    }
                }
    
                // We only arrive here if the lifecycle channel closed without
                // first sending the shutdown event, which is unexpected.
                process::abort();
            }
            None => {
                // We did not receive a lifecycle channel, exit abnormally.
                error!("No lifecycle channel received, exiting.");
                process::abort();
            }
        }
    }
    

    C++

    #include <fuchsia/process/lifecycle/cpp/fidl.h>
    
    // ...
    
    // Implementation of the fuchsia.process.lifecycle FIDL protocol
    class LifecycleHandler : public fuchsia::process::lifecycle::Lifecycle {
     public:
      explicit LifecycleHandler(async::Loop* loop) : loop_(loop) {
        // Get the PA_LIFECYCLE handle, and instantiate the channel with it
        zx::channel channel = zx::channel(zx_take_startup_handle(PA_LIFECYCLE));
        // Bind to the channel and start listening for events
        bindings_.AddBinding(
            this, fidl::InterfaceRequest<fuchsia::process::lifecycle::Lifecycle>(std::move(channel)),
            loop_->dispatcher());
        FX_LOGS(INFO) << "Lifecycle channel received.";
      }
    
      // This is the Stop event we must override - see the pure virtual function we
      // need to override at the declaration of fuchsia::process::lifecycle::Lifecycle
      void Stop() override {
        FX_LOGS(INFO) << "Received request to stop. Shutting down.";
        // Shut down our loop - it's important to call Shutdown() here vs. Quit()
        loop_->Shutdown();
        // Close the binding
        bindings_.CloseAll();
      }
    
     private:
      async::Loop* loop_;
      fidl::BindingSet<fuchsia::process::lifecycle::Lifecycle> bindings_;
    };
    

從父項開始

元件資訊清單可讓您將子項標示為 eager,這會使元件架構以隱含方式 再由父項開始建立該子項

如果熱銷子項因任何原因無法啟動 (例如缺少元件), 而元件管理工具會顯示下列行為:

  • 如果父項不是根元件,父項會啟動,但 和任何繫結至該繫結元件的元件都會觀察到連線中斷 (就像 其他失敗的繫結)。
  • 如果父項為根元件,元件管理員就會停止運作,且 錯誤訊息,例如:

    [component_manager] ERROR: protocol `fuchsia.component.CoreBinder` was not available for target `startup`:
        failed to resolve `fuchsia-pkg://fuchsia.com/your_component#meta/your_component.cm`:
        package not found: remote resolver responded with PackageNotFound
        For more, run `ffx component doctor `startup`
    

標示為 eager 的元件可能導致系統當機,但實際上沒有 如果其祖係也在根元件上標記 eager,這是 因為許多建構設定會建立包含 可用元件子集如要避免發生這個問題,請宣告這些 使用核心領域資料分割的元件,確保能 將測試版本和產品映像檔安全地排除

eager 元件也應位於同一個套件集中 因為元件會和它的父項同時啟動 父項。一般而言,eager 元件應位於產品的基本套件中 設定。

如要判斷套件是否位於基本套件集中,請執行下列指令 指令:

fx list-packages --verbose my-package

這個指令會輸出符合套件的套件集清單 找到。例如,system-update-checker 位於 baseuniverse 套件集:

$ fx list-packages --verbose system-update-checker
system-update-checker [base universe]

您也可以使用 --base 查看基本套件組合中的所有套件 選項:

fx list-packages --base

終止時重新啟動

元件資訊清單可讓您控管 使用 on_terminate 編輯元件。含有 「終止後重新啟動」政策設定可能導致系統安全重新啟動, 元件因任何原因終止 (包括成功結束時)。

如要啟用這項功能,請按照下列步驟操作:

  1. 在父項元件資訊清單中,將子項標示為 on_terminate: reboot

    // core.cml
    {
        children: [
            ...
            {
                name: "system-update-checker",
                url: "fuchsia-pkg://fuchsia.com/system-update-checker#meta/system-update-checker.cm",
                startup: "eager",
                on_terminate: "reboot",
            },
        ],
    }
    
  2. 將元件的路徑名稱新增至元件管理員的安全性政策許可清單 於 //src/security/policy/component_manager_policy.json5:

    // //src/security/policy/component_manager_policy.json5
    {
        security_policy: {
            ...
            child_policy: {
                reboot_on_terminate: [
                    ...
                    "/core/system-update-checker",
                ],
            },
        },
    }
    

錯誤

信號機制

當要求用戶端透過執行 Directory.Open() 要求,或 這個 公開的目錄 ,它會將伺服器端 zircon 物件控點,該物件會在轉送後提供能力。錯誤數 將導致該帳號代碼的物體被關閉並附帶縮寫。八卦 酬載一律為 Zircon 狀態碼。

轉送作業屬於延遲且非同步作業,因此訊息隨時都會送達 轉送作業啟動後

注意:一旦轉送作業成功,供應元件也可以關閉 帶有自選狀態碼這對客戶來說不可能 判斷物件是否遭到元件管理員關閉 或之後委任的其他方。

類似 libc 的呼叫 (例如 open()) 也適用相同的錯誤信號機制。

如需實際範例,請參閱「疑難排解」一節。

錯誤狀態碼

元件管理員可能會傳送下列錯誤代碼,表示失敗 轉送作業:

  • ZX_ERR_NOT_FOUND:無法將能力轉送至其中一個 。
    • 此設定未提供選用能力。
    • 路徑路徑上任何元件發生設定錯誤。
    • 供應元件與這個版本的 Fuchsia 版本不相容。
    • 服務元件程式中的錯誤。
    • 任何解析器執行器的錯誤 能完成轉送作業
  • ZX_ERR_ACCESS_DENIED:無法轉送能力,因為 要求元件無法存取該元件。例如:
    • 能力存在的政策許可清單,但 不包含要求元件。
    • 要求元件的權限大於所提供的值 (也就是要求以唯讀模式提供的目錄讀取/寫入資料)。
  • ZX_ERR_TIMED_OUT:其中一個轉送步驟已逾時。
  • ZX_ERR_INTERNAL:元件管理員本身發生未預期的錯誤, 指出平台發生錯誤。

NOT_FOUNDACCESS_DENIEDINTERNAL 錯誤都會重現 。軟體更新 甚至可能會變更能力的路徑 並維持該能力的可用性

轉送錯誤語意原則

  • 最低度:因為錯誤信號路徑是在元件之間共用 元件管理員和放送元件後,元件管理員會留下 供應元件要使用的錯誤空間
  • 用戶端觀點:雖然轉送方式取決於許多人 可能會因為各種原因 (包括錯誤) 而失敗 其他元件作者則會將錯誤語意個人化 要求用戶端和提出要求用戶端的需求例如: 中間元件作者中的使用者錯誤仍會傳回 NOT_FOUND 用於要求用戶端。

疑難排解

本節說明您在嘗試use與 透過建議的解決方案,從元件應用層面。

無法轉送功能

元件管理員會執行功能轉送,以找出 所指定能力的來源 (一旦元件嘗試存取 技術。如果轉送中的其中一個元件資訊清單,轉送作業可能會失敗 路徑設定不正確例如 offerexpose 宣告 路徑中的某些元件 不存在 無法解析鏈結。

請按照下列步驟檢查轉送失敗是否是管道關閉的原因:

  • 使用 ffx component route 檢查元件的轉送方式。這個 可與元件的路徑名稱搭配使用,也可與元件的 網址。例如:

    # check with the moniker
    ffx component route /core/ffx-laboratory:echo_realm/echo_client
    
    # check with a partial URL
    ffx component route echo_client.cm
    
  • 使用 ffx log 檢查元件記錄,找出開頭為 Failed to route 的郵件,瞭解轉送鏈結失敗的原因。例如:

    [echo_client][][W] protocol `fidl.examples.routing.echo.Echo` was not available for target `/core/ffx-laboratory:echo_realm/echo_client`:
        `fidl.examples.routing.echo.Echo` was not offered to `/core/ffx-laboratory:echo_realm/echo_client` by parent
        For more, run `ffx component doctor /core/ffx-laboratory:echo_realm/echo_client`
    
  • 請查看封閉式頻道中的 Epitaph 頻道。八卦 最常見的轉送失敗設定是 ZX_ERR_NOT_FOUND

    [echo_client][][I] Connecting to Echo protocol failed with error
    "A FIDL client's channel to the service fidl.examples.routing.echo.Echo was closed: NOT_FOUND"
    

    詳情請參閱轉送錯誤

如需能力轉送失敗的獨立範例,請參閱 //examples/components/routing_failed

接收轉送錯誤

當能力轉送失敗時,基礎 FIDL 管道就會關閉。FIDL 通訊協定 如果管道已關閉,繫結會傳回錯誤狀態。假設 範例:

荒漠油廠

let echo = connect_to_protocol::<EchoMarker>()
    .context("Failed to connect to echo service")?;
let res = echo.echo_string(Some("Hippos rule!")).await;
match res {
    Ok(_) => { info!("Call succeeded!"); }
    Err(fidl::Error::ClientChannelClosed { status, service_name } => { 
        error!("Channel to service {} was closed with status: {}", service_name, status); 
    } 
    Err(e) => {
        error!("Unexpected error: {}", e);
    }
};

C++

fuchsia::examples::EchoPtr echo_proxy;
auto context = sys::ComponentContext::Create();
context->svc()->Connect(echo_proxy.NewRequest());

// Sets an error handler that will be called if an error causes the underlying 
// channel to be closed. 
echo_proxy.set_error_handler([&loop](zx_status_t status) { 
  printf("Channel was closed with status: %d\n", status); 
  // ... 
}); 

echo_proxy->EchoString("Hippos rule!", [&](std::string response) {
  // ...
});

如要判斷管道停用的根本原因,可以查看 可選擇的 epitaph 語言。如果想擷取 請按照以下說明操作:

荒漠油廠

let stream = echo.take_event_stream();
match stream.next().await {
    Some(Err(fidl::Error::ClientChannelClosed { status, .. })) => {
        info!("Channel was closed with epitaph: {}", status);
    }
    Some(m) => {
        info!("Received message other than epitaph or peer closed: {:?}", m);
    }
    None => {
        info!("Component failed to start or channel was closed by server");
    }
}

C++

echo_proxy.set_error_handler([&loop](zx_status_t status) {
  // If an Epitaph was present on the channel, its error value will be passed as
  // the parameter.
  printf("Channel was closed with epitaph: %d\n", status);
});

無法啟動元件

如果能力轉送成功,您可能會遇到錯誤,但發生問題 是否要解析或啟動元件錯誤訊息格式 取決於元件執行器

  • 如果是 ELF 執行元件,請使用 ffx log --filter component_manager 查看元件管理員記錄。尋找開頭為 Failed to start component 的訊息。例如:

    [component_manager] WARN: Failed to start component
    `fuchsia-pkg://fuchsia.com/components-routing-failed-example#meta/echo_server_bad.cm`:
    unable to load component with url "fuchsia-pkg://fuchsia.com/components-routing-failed-example#meta/echo_server_bad.cm":
    error loading executable: "reading object at \"bin/routing_failed_echo_server_oops\" failed:
    A FIDL client's channel to the service fuchsia.io.File was closed: PEER_CLOSED"
    
  • 至於其他執行器,請查看執行元件元件的記錄。個人中心 執行下列指令來進行這項操作:

    ffx log --tags runner-name
    

如要解決這個問題,請確認下列事項:

  • 元件資訊清單中的 program 宣告如下: 確保設定正確例如,確認二進位檔路徑的拼字正確無誤 正確。
  • 二進位檔本身和所有啟動元件所需的其他資源 包含在套件內。

舉例來說,因設定錯誤而無法啟動的元件示例 請參閱 //examples/components/routing_failed

元件已終止或關閉管道

如果您確認轉送成功,並啟動了元件 ,您可能已經遇到來源元件關閉的問題 管道本身這可能會在元件執行期間發生 元件終止的副作用

如果元件因當機而終止,您可以查看當機報告 包含傾印中元件名稱的 ffx log 中:

[33860.645][klog][klog][I] crashsvc: exception received, processing
[33860.645][klog][klog][I] <== fatal : process echo_client.cm[21090] thread initial-thread[21092]
<stack trace follows...>

如果來源元件自行關閉管道,以下提供一些秘訣 疑難排解原因:

  • 如要瞭解錯誤訊息,請參閱來源元件的記錄檔
  • 使用 ffx debug fidl 檢查 FIDL 連線流量: fidlcat 表示錯誤或非預期的行為。