在 Fuchsia 中進行 Linux syscall

本頁說明 Starnix 如何處理 Linux 系統呼叫 也就是 Fuchsia 的「Sycalls」。

Zircon 程序中 Linux 程式的位址空間

Linux 程式必須先載入 Zircon 才能發出系統呼叫 上傳資料集之後,您可以運用 AutoML 自動完成部分資料準備工作starnix_kernel 是元件負責兩個 在 Fuchsia 中載入及執行 Linux 程式。

starnix_kernel 會先建立 Zircon 程序,且位址空間為 然後分成兩半上半部是共用位址空間,下半部則是 私人位址空間starnix_kernel 會設定 Zircon 處理程序如下:

  • Linux 程式會載入處理程序的私人位址空間 (下半部)。
  • starnix_kernel 本身會載入程序的共用位址空間 (上半部)。

位址空間的 Linux 部分採用的建構方式與 正好符合 Linux 系統的預期版面配置 計畫。

Zircon 程序的地址空間

圖 1. 透過 Linux 程式載入的 Zircon 程序位址空間 和starnix_kernel

共用的 Starnix 執行個體

每個 Linux 程式都會載入獨立的 Zircon 程序和 執行緒是在專屬的 Zircon 執行緒中執行。不過,這個 Pod 中所有的 Zircon 程序 Starnix 容器會共用相同的 starnix_kernel 例項。 這個共用的 starnix_kernel 執行個體會管理 Linux 程式的所有狀態 例如檔案描述元資料表、全域虛擬檔案系統 還有執行緒表格

進一步瞭解 Zircon 程序之間共用的狀態 執行 starnix_kernel,請參閱受限模式 RFCShareableProcessState 結構體。

共用的 Starnix 執行個體

圖 2. starnix_kernel 執行個體會由所有 Linux 程序共用 同一個容器中的應用程式

在一般執行模式下,這些程序中的任何執行緒皆可存取 共用位址空間然而,在受限執行模式下,執行緒只能 即可存取所屬程序的私人位址空間。也就是說 Linux 程式正在執行程序,Zircon 限制其執行緒的存取權 在過程中,位址空間的 Linux (私人) 部分。

嚴格篩選模式

圖 3. 嚴格篩選模式可防止 Linux 執行緒存取 位址空間的 starnix_kernel 部分。

以受限模式執行 Linux 程式

將 Linux 程式載入處理程序後,starnix_kernel 會指示 Zircon 即可開始相關程序程序開始在 starnix_kernel 中執行 部分進入 syscall 調度迴圈。

starnix_kernel 會檢查工作狀態,判斷要在哪裡進入 Linux 計畫。以下顯示的 restricted_enter() 方法可用來輸入 安裝在 Linux 程式中 (如需詳細資訊,請參閱 Zircon 中的 zx_restricted_enter syscall)。

// Enter the restricted portion of the address space at the location pointed to by `rip`.
restricted_enter(task.registers.rip);

不過,完整的呼叫比上述範例更複雜。 方法會透過個別的獨立輸入 名為「邊車」的 VMO如要進一步瞭解這個 VMO「補充資訊」,其中儲存了 zx_restricted_state 物件,包含受限制使用的架構狀態 模式,請參閱 Zircon 中的「zx_restricted_bind_state」Sycall)。

呼叫 restricted_enter() 方法後,Zircon 會記下執行緒事件 進入受限模式,並將執行緒設為僅限存取 再跳到 Linux 程式碼

Linux syscall 的流程

此程序會繼續執行,直到 Linux 程式發出 syscall 為止。 用來執行系統呼叫的組合指示會因架構而異,但 導致一切的控制權轉交給 Zircon。Zircon 檢查執行緒時 您會發現執行緒處於受限模式,而非處理系統呼叫 這時 Zircon 會立即跳到 Starnix。

為了找出 Linux 程式所做出的 syscall,Starnix 會讀取受限程式碼, 模式登錄狀態,用來儲存 Zircon。接著 Starnix 會為此叫用處理常式 特定系統呼叫這個系統呼叫的結果會儲存至嚴格篩選模式 註冊狀態

當 Syscall 傳回後,Starnix 再次指示 Zircon 可放入執行緒 切換至受限模式此迴圈會持續到執行緒結束為止。

Linux syscall 的流程

圖 4. Zircon 程序中 Linux syscall 的流程。

以下是圖 4 中資料流的虛擬程式碼表示法:

loop {
  // Enter restricted mode at the instruction pointer address.
  restricted_enter(task.registers.rip);

  // Update the register state of the task to match what Zircon saved before exiting
  // restricted mode.
  task.registers = thread_state_general_regs_t::from(&restricted_state);

  // Figure out which syscall was made based on the appropriate register value.
  let syscall = Syscall::from_number(task.registers.rax);

  // Execute the syscall for the current task.
  execute_syscall(task, syscall);

  // After handling the syscall, check for signals, exit status, etc. before
  // continuing the loop.
  if let Some(exit_status) = process_completed_syscall(task)? {
      return Ok(exit_status);
  }
}