RFC-0240:物件上的非同步作業

RFC-0240:非同步作業是針對物件
狀態已接受
區域
  • 核心
說明

定義 Zircon 中的非同步作業如何與物件和處理程序互動。

Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2024-02-06
審查日期 (年-月-日)2024-03-11

摘要

這表示 Zircon 核心中的非同步作業會在核心物件上執行,且不會與用於註冊這些作業的句柄相關聯。

提振精神

Zircon 會定義核心物件的非同步等待作業。我們想新增具有副作用的非同步作業,例如非同步管道讀取作業。為此,我們需要確定這些作業的語意,以及這些作業與相關句柄和/或物件的其他作業互動情形。

以 object_wait_async 為例,這是非同步作業的範例,可在物件狀態變更時產生 port 封包。目前的 API 會嘗試將在基礎物件上執行的作業 (如名稱所示) 與用於識別作業的句柄分開。

相關人員

Zircon

協助人員:

FEC 指派的人員,負責引導此 RFC 通過 RFC 程序。

審查者:

  • maniscalco@google.com
  • cpu@google.com
  • abarth@google.com

諮詢:

社會化:

需求條件

定義非同步核心作業的關係,包括 object_wait_async 和處理互動,例如轉移和關閉句柄。

設計

總覽

Zircon 中的非同步作業會對物件 (而非句柄) 運作。物件狀態的變更可能會影響這些作業。用於參照物件的句柄若有變更,不會影響非同步作業的行為,但請注意,下文會特別說明管道的區隔。註冊後,非同步作業會持續執行,直到完成、明確取消或關閉所有物件句柄為止。用於取消通訊埠上非同步作業的設施已更新,以反映新的語意。

Syscall 異動

object_wait_async

  • 從說明文件和後端程式碼中移除「如果句柄已關閉,則與其相關聯的作業也會終止,但佇列中已有的封包不會受到影響。」這句話。

註冊後,非同步等待作業會持續有效,直到物件狀態變更為符合觀察條件、等待作業明確取消,或物件的所有句柄都關閉為止。

port_cancel

  • 請淘汰 port_cancel,並提供 port_cancel_key 做為替代方案,這個替代方案會使用指定的鍵,取消指定通訊埠上的所有作業。簽名:
zx_port_cancel_key(zx_handle_t port, uint32_t options, uint64_t key)

這也是新增 options 參數的好機會,因為 port_cancel 目前缺少這個參數。

另一種做法是更新 port_cancel 的實作方式,以便忽略 source 參數。在實際操作中,可取消的連接埠作業會以每項作業的專屬索引鍵註冊。這個鍵通常是指派用於處理作業本身的資料結構的位址。有證據顯示這項變更是安全的 (請參閱「向下相容性」一節),但要安全地部署這項變更,並在使用相同名稱的情況下回收 options 插槽,將會相當困難。使用新名稱為作業命名,可讓我們逐步更新程式碼,並追蹤舊作業的呼叫端。

object_wait_oneobject_wait_manyhandle_close

  • 明確記錄的功能組合沒有任何變更。說明在同步等待期間關閉句柄已淘汰。

這些是同步等待,等待以句柄識別的一或多個物件。我們目前的說明文件指出,關閉句柄會透過這些作業取消任何目前待處理的等待作業,但這本質上是會有競爭狀態,因為一個關閉句柄的執行緒無法得知另一個呼叫 object_wait_{one,many} 的執行緒是否已解決該句柄。handle_closeobject_wait_{one,many} 競爭的程式可以觀察 ZX_ERR_CANCELEDZX_ERR_BAD_HANDLE。我們可以提供無競爭的取消方式,藉由將句柄與物件解除關聯 (https://fxrev.dev/949517),或讓程式更容易等待多個條件,以便在使用者空間中同步處理句柄存取權。由於目前無法明確指出在作業中,哪些句柄需要有效,因此這項提案不會變更行為。

channel_callchannel_call_etc

  • 明確記錄的功能組合沒有任何變更。

這些目前具有隱含的說明文件,說明與 ZX_ERR_CANCELED 傳回值中的 object_wait_{one,many} 相同的取消行為。在一般情況下使用 handle_cancel 時,這些作業也會出現與這些作業相同的競爭問題,但由於這項作業會揭露更多內部詳細資料,因此可以編造出在實際情況下不會競爭的情況。呼叫端無法為 channel_call 的內部等待時間設定超時以外的參數,因此程式無法使用多個條件來協調關機作業。為了提供無競爭的取消作業,我們需要定義另一個作業,例如 https://fxrev.dev/949517 中提出的作業。與 object_wait_{one,many} 一樣,雖然目前的行為令人懷疑,但本 RFC 並未提出任何變更,以便這些作業的行為。

管道排序

管道在 Fuchsia 系統架構中扮演獨特的角色,目前在核心中提供特殊情況邏輯,以驗證管道句柄的作業只有在該句柄由呼叫程序擁有時才會成功。管道在系統中廣泛用於交換資料和功能。這些檢查的目的是在不同信任層級運作的程序之間傳遞句柄時,維持管道的機密性和完整性。一旦程序取得管道句柄,就保證可獨佔該端點的讀取和寫入訊息存取權。為了保留這個屬性,並且讓這個屬性符合本 RFC 中提出的語意,並允許日後的非同步管道作業,我們可以利用管道的另一個屬性,也就是管道端點只有一個句柄。對管道句柄的轉移作業可視為對物件本身的作業,我們可以導致管道上的待處理變異作業,或轉移嘗試失敗。

觀察物件狀態 (例如 READABLE 和 WRITABLE 信號) 不會違反我們重視的屬性。即使在轉移此句柄後,如果程式仍可觀察管道的可讀或可寫狀態,這也是可接受的做法,因為這類資訊不會傳送關於系統狀態的功能或重要機密資訊。也就是說,object_wait_async 作業不需要針對含有此提案的管道做出不同的行為。未來對管道執行的作業 (例如非同步讀取) 都必須考量與轉移作業的互動情形。

實作

這項變更對使用者空間的主要實際影響,是取消在連接埠物件上等待的非同步物件。非同步調度器程式庫需要為可取消作業分配及追蹤專屬鍵,而這正是它們目前所做的事。如果不使用物件,則不必再追蹤句柄值。將第二個參數從句柄值變更為文字常值 0,即可將對 port_cancel 的呼叫機械式轉譯為 port_cancel_key

在核心方面,觀察者、端口和物件表示法之間的關係非常微妙。這項變更會在取消程序中移除對句柄表格鎖定項目的依附性,並在日後重構句柄管理邏輯。短期內,實作這項變更最簡單的方式,就是使用空值 Handle 註冊非同步等待,並在觀察器觸發時,將註冊的等待標記為已取消,並清除觀察器狀態,以便執行非同步等待取消作業。

成效

這項變更可減少不同步作業的核心簿記量,並降低對句柄表格鎖定機制的壓力,這可能有助於在特定類型的負載下。我們目前的微型基準測試顯示,簡單的原型實作方式並未帶來重大變化。使用者空間程式庫也可以儲存較少的資訊,以便取消已註冊的非同步作業。

人體工學

因此,如果應用程式想取消個別非同步作業,就必須追蹤關鍵值。在實務中,調度器實作項目已執行這項操作 (除了保留句柄值)。

回溯相容性

這會以微妙的方式變更 object_wait_asyncport_cancel 的說明和實際語意。這項異動不會影響維護下列屬性的計畫:

  • 非同步等待會以每個等待的專屬鍵註冊
  • 等待的物件句柄未關閉或轉移,而已註冊的等待仍處於待處理狀態

這些說法在今天仍大致正確。非同步等待通常會與狀態分配相關聯,以便處理等待結果。這些等待的 key 通常會根據此分配的位址計算,該位址必須在位址空間中或其他形式的唯一 ID 中保持不變。句柄通常會儲存在擁有的資料結構中,關閉句柄需要經過某種關閉程序,清除對該句柄的參照。

https://fxrev.dev/984701 是原型,會忽略 source 參數至 port_cancel。其中一個 Zircon 核心測試會刻意重複使用金鑰,樹狀結構中的所有其他測試案例則會在未經修改的情況下通過。這表示對 port_cancel 所做的變更與目前的程式碼相容。

https://fxrev.dev/986494 是相關的原型,可將註冊的句柄與觀察器的生命週期解耦。這項測試會評估我們是否會依靠關閉句柄來取消未完成的非同步等待作業。我們所有現有的測試在進行此變更後,無須修改即可通過測試,這表示這項行為變更與目前的程式碼相容。

安全性考量

這項提案可讓程序在某些情況下,觀察物件狀態的變更,而不再需要句柄。在這個模型中,句柄代表啟動非同步作業的能力。一般來說,我們不會保證句柄會代表物件的專屬存取權,除非是具有不可重複句柄 (最明顯的例子是管道) 的物件,才會發生這種情況。

對於大多數授予可複製的句柄的物件類型,如果該句柄的擁有者不瞭解該句柄的完整建立或信任來源轉移記錄,就無法推斷該句柄上存在哪些其他句柄和作業。如果我們允許在轉移至該物件的句柄後,讓非同步作業持續存在於該物件上,這項分析就不會有太大變化,因為註冊程式可能會輕易保留重複的句柄。

特別針對頻道,我們一律會授予不可重複的帳號代碼,因此我們保證會將管道帳號代碼的程序獨占存取權。允許管道代管帳號的前擁有者,在管道轉移後首次變得可讀時,觀察頻道代管帳號的行為,可能不會造成安全性上的敏感問題。我們在引入與透過管道傳送的訊息內容互動的作業時,必須謹慎行事,例如只允許目前的句柄擁有者執行這些作業。

測試

我們會使用 Zircon 的核心測試套件,測試對 port 和非同步物件行為的變更。我們會檢查程式庫及其測試套件,評估程式庫的相容性,尤其是調度器程式庫。我們現有的整合套件會透過新行為的原型 (請參閱「回溯相容性」一節) 通過測試,因此我們可以密切監控這些測試,並將變更部署至 object_wait_async

說明文件

您必須為新的 port_cancel_key 新增系統呼叫說明文件,並按照設計部分所述更新現有作業的說明文件。

缺點、替代方案和未知事項

以句柄為基礎的替代語意

另一個替代做法是繼續將非同步作業與其啟動句柄建立關聯 (如部分現有文件所述),並在新增非同步作業時維持這項關聯。這會使這些新作業變得更複雜,並使重構和最佳化核心的句柄管理邏輯變得更加困難,同時對應用程式邏輯提供的效用不大。

port_cancel 重複使用

我們可以將現有 port_cancelsource 參數,替換成相同大小的 options 參數,而非定義新的 port_cancel_key 系統呼叫。在過渡期間,這個欄位不支援任何選項,且允許使用句柄值。在這段轉換期間,我們會找出並更新所有 port_cancel 的呼叫端,以便在這個欄位中傳遞 0 值,而非目前提供的句柄值。在轉換期結束後,我們會開始強制將欄位設為零,並開始引入選項值。這種方法的難處在於,可能很難偵測到所有呼叫端何時更新為新語意。在實際操作中,很少會在非同步等待作業觸發前取消該作業,因此對 port_cancel 進行動態分析時,可能不會找到只有在非預期錯誤處理或時間情況下才會觸發的呼叫端。

既有技術與參考資料

Zircon 句柄解析模型 - 2020 年草擬的 RFC,探討這些問題中的部分內容。

zx_handle_cancel - 草擬 RFC,定義了一種機制,可在句柄上取消同步作業,避免句柄解析競爭。

非同步迴圈金鑰管理

zxwait 鍵管理

fuchsia-async 金鑰管理