本指南提供最佳做法,協助 Fuchsia 開發人員在 Starnix 中使用各種處理序間通訊 (IPC) 模式時,瞭解取捨之處。
在 Starnix 中發出 FIDL 呼叫的最佳做法:
如要瞭解指南中列出的最佳做法的實際導入方式,請參閱「程式碼範例」。
在 Starnix 中選取 FIDL 繫結
本節提供決策樹,協助開發人員選擇同步和非同步 FIDL 繫結。
您無法直接從 Starnix 系統呼叫 (syscall) 叫用非同步 FIDL 呼叫。這是因為 Starnix 中的執行緒需要將控制權交還給 Linux。也就是說,您無法在 Starnix 中註冊非同步回呼處理常式。
建議您改用下列任一模式,在 Starnix 中進行非同步 FIDL 呼叫:
- 在系統呼叫處理常式中同步執行所有作業。
- 在主要執行緒上產生 Future。
- 使用執行緒產生器產生自己的執行緒。
圖 1. 這個流程圖可視覺化呈現 Starnix 中選取 FIDL 繫結的決策過程。
選擇其中一種模式時,應考量哪些因素?
針對提供 Linux 程式系統呼叫的 FIDL 呼叫,選取「同步 FIDL 繫結」。
這通常是預設做法。當需要從「使用者」執行緒發出 FIDL 呼叫,以便為 Linux 程式的系統呼叫提供服務時,就會套用此方法。如需程式碼範例,請參閱「Starnix 中的同步 FIDL 繫結」。
不過,請注意,如果要求遭到長時間封鎖,Linux 信號就無法終止提出要求的 Starnix 執行緒。Zircon 團隊正在努力新增一種機制,用於中斷在
zx_channel_call
中遭到封鎖的執行緒。不過,在 Starnix 支援這類機制之前,您必須小心在同步 FIDL 呼叫中封鎖 Starnix 執行緒的時機。針對與任何系統呼叫無關的 FIDL 呼叫,選取「非同步 FIDL 繫結」。
請特別考慮下列情況:
- 需要同時組合及等待多個呼叫。
- FIDL 通訊協定可喚醒處於暫停狀態的容器。
- FIDL 通訊協定使用了一些在同步繫結中難以實作的功能,例如要求它提供其他通訊協定。
- 作業必須可由其他 Linux 執行緒的訊號中斷。如果 FIDL 呼叫需要可中斷,就不能是同步呼叫;您必須能夠在
Waiter
上封鎖系統呼叫執行緒,以便接收信號。
針對非同步 FIDL 繫結,有兩種建立執行緒的技巧可供考量:
在主執行緒的執行程式上產生 Future,這通常是預設情況。如需程式碼範例,請參閱「直接在主執行緒的執行緒上產生 Future」。
產生自己的執行緒,這是個例外情況,因為開發人員可以控制執行緒的執行時間,且可以在同一個執行緒上混合同步和非同步工作,而不會阻斷 Starnix 其他部分產生的非同步工作。如需程式碼範例,請參閱「產生執行自身執行者的個別執行緒」。
在 Starnix 中選取命名空間
本節會說明如何使用核心和容器命名空間,在 Starnix 中存取 Fidl 通訊協定。
單一 Starnix 程式執行個體會充當兩個元件:一個是核心元件 (由 Starnix 執行器執行),另一個是容器元件 (由核心元件執行)。也就是說,有兩個地方可能會將功能路由至同一個程式供其使用。要使用的 API 取決於能力的導向位置。(如要進一步瞭解 Starnix 中的命名空間,請參閱「在 Fuchsia 中執行 Linux 系統呼叫」一文。
請考慮 Starnix 中的下列命名空間,可提供對 FIDL 通訊協定的存取權:
- Kernel 命名空間:這個命名空間可透過一般
fuchsia_component
呼叫存取。 - 容器命名空間:如需程式碼範例,請參閱「Starnix 中的容器命名空間」。
程式碼範例
本節提供程式碼範例,說明本指南中所述的不同 IPC 模式:
這些範例來自 Starnix 核心的各個部分,可提供指南中所述最佳做法的實際實作方式。
Starnix 中的同步 FIDL 繫結
本節提供程式碼範例,說明直接從系統呼叫處理常式中處理 FIDL 呼叫的標準方法,代表同步 FIDL 繫結的典型實作方式。
在 Starnix 中連線至同步 Proxy 的程式碼範例:
// Check whether we actually have access to a role manager by trying to set our own
// thread's role.
let role_manager = connect_to_protocol_sync::<RoleManagerMarker>().unwrap();
let role_manager = if let Err(e) =
set_thread_role(&role_manager, &*fuchsia_runtime::thread_self(), Default::default())
{
log_warn!("Setting thread role failed ({e:?}), will not set thread priority.");
None
} else {
log_info!("Thread role set successfully.");
Some(role_manager)
};
(來源://src/starnix/kernel/runner/container.rs
)
在 Starnix 中使用同步 Proxy 的程式碼範例:
pub fn set_thread_role(
role_manager: &RoleManagerSynchronousProxy,
thread: &zx::Thread,
policy: SchedulerPolicy,
) -> Result<(), Errno> {
let role_name = policy.kind.role_name();
log_debug!(policy:?, role_name; "setting thread role");
let thread = thread.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)?;
let request = RoleManagerSetRoleRequest {
target: Some(RoleTarget::Thread(thread)),
role: Some(RoleName { role: role_name.to_string() }),
..Default::default()
};
let _ = role_manager.set_role(request, zx::MonotonicInstant::INFINITE).map_err(|err| {
log_warn!(err:?; "Unable to set thread role.");
errno!(EINVAL)
})?;
Ok(())
}
(來源://src/starnix/kernel/task/scheduler.rs
)
Starnix 中的非同步 FIDL 繫結
本節包含程式碼範例,說明在 Starnix 中處理非同步 FIDL 呼叫的兩種不同方法:
直接在主執行緒的執行緒上產生 Future
以下程式碼範例會直接在 Starnix 主執行緒的執行緒上產生未來:
current_task.kernel().kthreads.spawn_future(async move {
let _ = {
// 1. Lock the state to update `abort_handle` when the timer is still armed.
// 2. MutexGuard needs to be dropped before calling await on the future task.
// Unfortunately, std::mem::drop is not working correctly on this:
// (https://github.com/rust-lang/rust/issues/57478).
let mut guard = self_ref.state.lock();
if !guard.armed {
return;
}
let (abortable_future, abort_handle) = futures::future::abortable(
self_ref.start_timer_loop(kernel_ref.kthreads.system_task(),thread_group),
);
guard.abort_handle = Some(abort_handle);
abortable_future
}
.await;
});
(來源://src/starnix/kernel/task/interval_timer.rs
)
產生執行自身執行者的個別執行緒
程式碼範例:產生獨立的 kthread
,執行其執行緒:
kernel.kthreads.spawner().spawn(|_, _| {
let mut executor = fasync::LocalExecutor::new();
let scheduler = ThroughputScheduler::new();
let mut view_bound_protocols = Some(view_bound_protocols);
let mut view_identity = Some(view_identity);
let mut maybe_view_controller_proxy = None;
executor.run_singlethreaded(async move {
(來源://src/starnix/kernel/device/framebuffer_server.rs
)
Starnix 中的容器命名空間
本節包含程式碼範例,說明如何存取位於容器元件中的容器式 FIDL 通訊協定。
程式碼範例:說明如何在容器命名空間中連線至 CryptManagement
通訊協定:
let crypt_management_proxy = current_task
.kernel()
.connect_to_protocol_at_container_svc::<CryptManagementMarker>()
.map_err(|_| errno!(ENOENT))?
.into_sync_proxy();
(來源://src/starnix/kernel/vfs/file_object.rs
)