Zircon 核心概念

簡介

核心會管理多種不同類型的物件。透過系統呼叫直接存取的類別,是實作 Dispatcher 介面的 C++ 類別。這些是透過 kernel/object 實作。其中許多是獨立的較高層級物件。lk

系統呼叫

使用者空間程式碼會透過系統呼叫與核心物件互動,幾乎只會透過句柄互動。在使用者空間中,句柄會以 32 位元整數 (類型 zx_handle_t) 表示。執行系統呼叫時,核心會檢查句柄參數是否參照呼叫程序句柄表中存在的實際句柄。核心會進一步檢查句柄是否為正確類型 (將執行緒句柄傳遞至需要事件句柄的系統呼叫會導致錯誤),以及句柄是否具有所要求作業所需的權利。

從存取權的角度來看,系統呼叫可分為以下三類:

  1. 只有極少數的呼叫沒有限制,例如 zx_clock_get_monotonic()zx_nanosleep() 可能會由任何執行緒呼叫。
  2. 以句柄做為第一個參數的呼叫,表示要對其執行的物件,這類呼叫占大多數,例如 zx_channel_write()zx_port_queue()
  3. 建立新物件但不使用句柄的呼叫,例如 zx_event_create()zx_channel_create()。這些項目的存取權 (以及相關限制) 會由包含呼叫程序的工作控管。

系統呼叫由 libzircon.so 提供,這是 Zircon 核心提供給使用者空間的「虛擬」共用程式庫,也稱為虛擬動態共用物件或 vDSO。這些是 zx_noun_verb()zx_noun_verb_direct-object() 格式的 C ELF ABI 函式。

系統呼叫會在 //zircon/vdso 中以自訂的 FIDL 格式定義。這些定義會先由 fidlc 處理,然後再由 zither 處理,後者會採用 fidlc 的 IR 表示法,並輸出各種格式,用於 VDSO、核心等的黏合劑。

控制代碼權利

物件可能會有多個 (在一個或多個程序中) 參照的句柄。

對於幾乎所有物件,當最後一個參照物件的開放式句柄關閉時,物件會遭到銷毀,或進入無法復原的最終狀態。

您可以將句柄寫入管道 (使用 zx_channel_write()),或使用 zx_process_start() 將句柄做為新程序中第一個執行緒的引數傳遞,藉此將句柄從一個程序移至另一個程序。

系統會根據與該帳號代碼相關聯的權利,決定可對該帳號代碼或其參照的物件採取哪些動作。兩個參照相同物件的句柄可能具有不同的權利。

您可以使用 zx_handle_duplicate()zx_handle_replace() 系統呼叫,取得與傳入的句柄相同的物件額外句柄,並視需要減少權限。如果該控制代碼是該物件的最後一個控制代碼,zx_handle_close() 系統呼叫會關閉控制代碼,並釋放該控制代碼所參照的物件。zx_handle_close_many() 系統呼叫會以類似方式關閉句柄陣列。

核心物件 ID

核心中的每個物件都有「核心物件 ID」或簡稱「koid」。這是 64 位元不帶正負號整數,可用於識別物件,且在執行系統的整個生命週期中皆為唯一。這特別表示 koid 絕不會重複使用。

有兩個特殊的 koid 值:

ZX_KOID_INVALID 的值為零,用於做為「空值」哨兵。

ZX_KOID_KERNEL 只有一個核心,且有專屬的 koid。

核心產生的 koid 只使用 63 位元 (足夠使用)。這會保留空間,讓您透過設定最高位元組,人工分配 koid。核心產生的 koid 分配序列未指定,且可能會變更。

人工 koid 的存在目的是支援識別人工物件 (例如追蹤中的虛擬執行緒),以供工具使用。人工 koid 的分配方式由各個程式自行決定,本文件不會強制規定任何規則或慣例。

執行程式碼:工作、程序和執行緒。

執行緒代表位於程序所在的位址空間內執行的執行緒 (CPU 登錄、堆疊等)。程序由工作擁有,而工作會定義各種資源限制。工作由父項工作擁有,一直到根工作,這是在啟動時由核心建立,並傳遞至 userboot 的,也就是開始執行的首個使用者空間程序

如果沒有工作句柄,處理序中的執行緒就無法建立其他處理序或工作。

在核心層之上的使用者空間設施和通訊協定會提供程式載入功能。

請參閱:zx_process_create()zx_process_start()zx_thread_create()zx_thread_start()

訊息傳遞:Socket 和管道

Sockets 和管道都是雙向和雙端的 IPC 物件。建立 Socket 或 Channel 會傳回兩個句柄,一個參照物件的每個端點。

套接字是資料流導向的,資料可以以一或多個位元組的單位寫入或讀取。可能會發生短寫入 (如果 Socket 的緩衝區已滿) 和短讀取 (如果要求的資料多於緩衝區) 的情況。

管道是以資料包為導向,其訊息大小上限由 ZX_CHANNEL_MAX_MSG_BYTES 指定,且最多可附加 ZX_CHANNEL_MAX_MSG_HANDLES 個句柄至訊息。不支援短讀取或寫入,也就是說,訊息要嘛符合,要嘛不符合。

當句柄寫入管道時,系統會將其從傳送程序中移除。當從管道讀取含有句柄的訊息時,系統會將句柄新增至接收程序。在這兩個事件之間,如果寫入的管道端點已關閉,則句柄會繼續存在 (確保所參照的物件會繼續存在),但如果寫入的管道端點已關閉,則會捨棄傳送至該端點的訊息,並關閉其中包含的任何句柄。

請參閱:zx_channel_create()zx_channel_read()zx_channel_write()zx_channel_call()zx_socket_create()zx_socket_read()zx_socket_write()

物件和信號

物件最多可有 32 個信號 (由 zx_signals t 類型和 ZX 定義的 SIGNAL 定義表示),這些信號代表目前狀態的資訊。舉例來說,管道和 Socket 可以是可讀或可寫。處理程序或執行緒可能會遭到終止。依此類推。

執行緒可能會等待信號在一或多個物件上啟用。

詳情請參閱「信號」。

等待:等待一個、等待多個和 Port

執行緒可能會使用 zx_object_wait_one() 等待信號在單一句柄上啟用,或使用 zx_object_wait_many() 等待多個句柄的信號。這兩個呼叫都允許逾時,即使沒有待處理的信號,也會傳回。

根據計時器鬆弛度,逾時時間可能會與指定的期限有所出入。詳情請參閱計時器延遲

如果執行緒要等待大量句柄,使用 Port 會更有效率。Port 是其他物件可繫結的物件,當信號在這些物件上斷言時,Port 會收到含有待處理信號資訊的封包。

請參閱:zx_port_create()zx_port_queue()zx_port_wait()zx_port_cancel()

事件、事件組合。

事件是最簡單的物件,除了收集有效信號外,沒有其他狀態。

事件組合是指一對可能彼此發出訊號的事件。事件配對的實用屬性是,當一組事件的其中一端消失 (所有相關的句柄都已關閉) 時,系統會在另一端宣告 PEER_CLOSED 信號。

請參閱:zx_event_create()zx_eventpair_create()

共用記憶體:虛擬記憶體物件 (VMO)

虛擬記憶體物件代表一組實體記憶體頁面,或頁面的潛在內容 (會在需要時延遲建立/填入)。

這些物件可使用 zx_vmar_map() 對應至程序的位址空間,並使用 zx_vmar_unmap() 取消對應。您可以使用 zx_vmar_protect() 調整對應頁面的權限。

您也可以直接使用 zx_vmo_read()zx_vmo_write() 讀取及寫入 VMOs。因此,對於一次性作業 (例如建立 VMO、將資料集寫入其中,並交給其他程序使用),您可以避免將這些項目對應至位址空間的成本。

位址空間管理

虛擬記憶體位址區域 (VMAR) 提供抽象層,用於管理程序的位址空間。在程序建立期間,系統會將根 VMAR 的句柄提供給程序建立者。該句柄是指跨整個位址空間的 VMAR。這個空間可透過 zx_vmar_map()zx_vmar_allocate() 介面劃分。zx_vmar_allocate() 可用來產生新的 VMAR (稱為子區或子項),用於將位址空間的部分區塊分組。

請參閱:zx_vmar_map()zx_vmar_allocate()zx_vmar_protect()zx_vmar_unmap()zx_vmar_destroy()

Futex

Futex 是用於使用者空間原子運算的核心原始元件,可實現高效的同步處理原始元件,例如 Mutex,只需在爭用情況下建立系統呼叫。通常只有標準程式庫的實作者才會對這些項目感興趣。Zircon 的 libc 和 libc++ 提供 C11、C++ 和 pthread API,用於以 Futex 實作的互鎖、條件變數等。

請參閱:zx_futex_wait()zx_futex_wake()zx_futex_requeue()