| RFC-0265:計數器:用於跨程序同步處理的簡單 Zircon 物件 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | 建議使用新的 Zircon 物件 zx::counter。 |
| 問題 | |
| Gerrit 變更 | |
| 作者 | |
| 審查人員 | |
| 提交日期 (年-月-日) | 2024-12-09 |
| 審查日期 (年-月-日) | 2025-02-12 |
問題陳述
這項 RFC 的動機是兩個不同的用途:Starnix 暫停和時間戳記圖形柵欄。雖然 Starnix 暫停是主要用途,但時間戳記圖形柵欄也是重要的次要用途。
Starnix 停權
我們需要讓 Starnix Kernel 和 Starnix Runner 協調運作,並在 Fuchsia 平台元件有待處理的訊息時,防止系統暫停運作。
這項作業包含三個程序:
部分 Fuchsia 平台元件,會在管道上傳送訊息。
Starnix 核心,需要讀取並處理該訊息。
Starnix Runner,其角色說明如下。
Starnix Runner 負責將所有執行緒放入 ZX_THREAD_SUSPENDED 狀態,並觀察 Fuchsia 平台元件傳送至 Starnix Kernel 的訊息,藉此暫停 Starnix Kernel。Starnix Runner 會做為 Proxy,代表 Starnix Kernel 接收訊息,然後在 Starnix Kernel 未暫停時轉送訊息,或在暫停時恢復並轉送訊息。
我們希望確保不會有任何「遺失的喚醒」,因此 Starnix Runner 和 Starnix Kernel 必須就是否有任何未處理或「傳輸中」的訊息達成共識,這些訊息應會阻止 Starnix Kernel 暫停。
目前 Starnix Runner 和 Starnix Kernel 是使用單一 zx::event 進行協調。這個事件用於指出有訊息正在傳輸。
以這種方式使用事件可正常運作,並避免喚醒遺失。不過,這項功能有項限制,就是一次最多只能代理一則訊息。如果沒有這項限制,Starnix Runner 就無法判斷 Starnix Kernel 是否已處理所有傳輸中的訊息,還是只處理已收到的訊息。
我們希望移除這項限制,支援多封傳輸中的郵件。為支援多個傳輸中訊息,我們需要某種方式來計算這些訊息,以便在計數達到零時暫停。
如果有多個傳輸中的訊息,單獨使用 zx::event 就不夠。不過,zx::event 加上共用整數就足夠了。如果 Starnix Runner 和 Starnix Kernel 共用位址空間,我們只要將整數儲存在記憶體中即可。不過,這些程序不會共用位址空間,因此我們需要將整數儲存在其他共用資源中,例如 VMO。由於這些程序位於相同的邏輯信任網域,因此完全可以透過 VMO 進行協調。不過,如果我們有新的 Zircon 物件,可做為具有計數的事件,就能簡化作業。
附有時間戳記的圖形柵欄
ZirconVmoSemaphore 用於實作含時間戳記的圖形柵欄。這項作業的運作方式類似 zx::event,外加一個時間戳記,指出物件發出信號的時間。圖像堆疊會為每個影格建立多個這類物件。每個 VMO 都會佔用一頁記憶體 (加上一些額外負荷)。使用 VMO 實作含時間戳記的圖形柵欄可行,但效率稍低。如果我們有一個類似 zx::event 加時間戳記的物件,就能做得更好。
另請參閱「VkFence」。
摘要
我們建議建立新的 Zircon 物件 zx::counter,該物件會保存整數、可遞增/遞減,並在值大於零或小於/等於零時發出信號。
利害關係人
主要利害關係人為 Zircon、Starnix 和 Graphics。
協助人員:davemoore@google.com
審查人員:emircan@google.com、lindkvist@google.com、mcgrathr@google.com
諮詢對象:adanis@google.com、eieio@google.com、rudymathu@google.com
討論:在 RFC 初稿發布前,我們已透過電子郵件與多位人員討論過這項提案的各種變化。另請參閱 go/zx-counter 的內部討論。
目標和需求
解決當前問題 - 啟用 Starnix Kernel 和 Starnix Runner,在 Fuchsia 平台元件有待處理的訊息時,協調並防止暫停。啟用圖形堆疊,以更有效率的同步工具,取代稍微較為耗用資源的 VMO 型 ZirconVmoSemaphore。
保持簡單 - 解決當下問題所用的任何方法都應保持簡單。請勿建構超出必要範圍的功能。如果出現更多用途,我們可以針對這些用途開發解決方案 (或建立不同的解決方案!)。
設計
我們推出了新的 Zircon 物件 Counter。計數器類似於事件,但具有可遞增、遞減、讀取或寫入的帶正負號 64 位元整數。
當計數器的值大於零時,訊號 ZX_COUNTER_POSITIVE 會經過判斷,而 ZX_COUNTER_NON_POSITIVE 則會取消判斷。當值小於或等於零時,ZX_COUNTER_POSITIVE 會取消斷言,而 ZX_COUNTER_NON_POSITIVE 會斷言。
雖然 Starnix 暫停用例可透過遞增和遞減作業 (判斷/取消判斷信號) 滿足,但 Timestamped Graphics Fence 用例需要讀取和寫入作業。做為柵欄的計數器會從零開始,代表未發出信號的柵欄。正值代表已發出信號的柵欄,值本身是柵欄發出信號的時間。
如要發出柵欄信號,圖形堆疊中的信號元件會使用寫入作業,將計數器的值設為目前時間 (請參閱 zx_instant_mono_t)。當計數器的值從零變更為這個正值時,系統會判斷 ZX_COUNTER_POSITIVE。圖形堆疊中的信號元件隨後可以讀取計數器的值,判斷柵欄發出信號的時間。
為支援這兩種用途,我們新增了四個系統呼叫 (zx_counter_create、zx_counter_add、zx_counter_read、zx_counter_write) 和兩個額外信號:
// Asserted when a counter's value is less than or equal to zero.
#define ZX_COUNTER_NON_POSITIVE __ZX_OBJECT_SIGNAL_4
// Asserted when a counter's value is greater than zero.
#define ZX_COUNTER_POSITIVE __ZX_OBJECT_SIGNAL_5
計數器是使用 zx_counter_create 建立:
## Summary
Create a counter.
## Declaration
zx_status_t zx_counter_create(uint32_t options, zx_handle_t* out);
## Description
zx_counter_create() creates a counter, which is an object that encapsulates
a signed 64-bit integer value that can be incremented, decremented, read, or
written.
When the value is greater than zero, the signal ZX_COUNTER_POSITIVE is
asserted. Otherwise ZX_COUNTER_NON_POSITIVE is asserted. Exactly one of
these two signals is always asserted, and never both at once.
The newly-created handle will have rights ZX_RIGHTS_BASIC, ZX_RIGHTS_IO, and
ZX_RIGHT_SIGNAL. The value will be zero and the signal
ZX_COUNTER_NON_POSITIVE will be asserted on the newly-created object.
## Rights
Caller job policy must allow ZX_POL_NEW_COUNTER.
## Return value
zx_counter_create() returns ZX_OK and a valid event handle (via *out*) on
success.
On failure, an error value is returned.
## Errors
ZX_ERR_INVALID_ARGS *out* is an invalid pointer, or *options* is non-zero.
ZX_ERR_NO_MEMORY Failure due to lack of memory.
There is no good way for userspace to handle this (unlikely) error.
In a future build this error will no longer occur.
您可以使用 zx_counter_add 遞增或遞減計數器:
## Summary
Add an amount to a counter.
## Declaration
zx_status_t zx_counter_add(zx_handle_t handle, int64_t amount);
## Description
zx_counter_add() adds amount to the counter referenced by handle.
After the result of the addition, if the counter's value is:
* less than or equal to zero - ZX_COUNTER_NON_POSITIVE will be asserted
and ZX_COUNTER_POSITIVE will be deasserted.
* greater than zero - ZX_COUNTER_POSITIVE will be asserted and
ZX_COUNTER_NON_POSITIVE will be deasserted.
## Rights
handle must have both ZX_RIGHT_READ and ZX_RIGHT_WRITE. Because a counter's
value could be determined by checking for ZX_ERR_OUT_OF_RANGE on a series of
carefully crafted zx_counter_add calls, there is no way to create a counter
that cannot be read, but which can be added.
## Return value
zx_counter_add() returns ZX_OK on success.
On failure, an error value is returned.
## Errors
ZX_ERR_WRONG_TYPE if handle is not a counter handle.
ZX_ERR_ACCESS_DENIED if handle does not have ZX_RIGHT_WRITE.
ZX_ERR_OUT_OF_RANGE if the result of the addition would overflow or underflow.
可以使用 zx_counter_read 讀取計數器:
## Summary
Read the value of a counter.
## Declaration
zx_status_t zx_counter_read(zx_handle_t handle, int64_t* value);
## Description
zx_counter_read() reads the value of the counter referenced by handle into the
integer value points at.
## Rights
handle must have ZX_RIGHT_READ.
## Return value
zx_counter_read() returns ZX_OK on success.
On failure, an error value is returned.
## Errors
ZX_ERR_WRONG_TYPE if handle is not a counter handle.
ZX_ERR_ACCESS_DENIED if handle does not have ZX_RIGHT_READ.
ZX_ERR_INVALID_ARGS if value is an invalid pointer.
計數器可使用 zx_counter_write 撰寫:
## Summary
Write the value to a counter.
## Declaration
zx_status_t zx_counter_write(zx_handle_t handle, int64_t value);
## Description
zx_counter_write() writes value to the counter referenced by handle,
asserting/deasserting signals as necessary.
Because concurrent operations on a counter may be interleaved with one another,
an implementation of a "counting semaphore" synchronization protocol should use
zx_counter_add() instead of a sequence of zx_counter_read(), modify,
zx_counter_write().
## Rights
handle must have ZX_RIGHT_WRITE.
## Return value
zx_counter_write() returns ZX_OK on success.
On failure, an error value is returned.
## Errors
ZX_ERR_WRONG_TYPE if handle is not a counter handle.
ZX_ERR_ACCESS_DENIED if handle does not have ZX_RIGHT_WRITE.
ZX_ERR_INVALID_ARGS if value is an invalid pointer.
實作
系統會透過幾個 CL (可能是一個) 在 Zircon 中實作計數器。Starnix 核心和 Starnix Runner,以及圖形堆疊稍後會更新為使用計數器。
各種語言繫結、FIDL 控制代碼支援、fidlcat/fidl_coded 等都會更新。
效能
我們預期 zx_counter_create 的效能會與zx_event_create類似。我們預期 zx_counter_add 和 zx_counter_write 的效能與 zx_object_signal 類似。
zx_counter_read 應與此類似,但可能也會執行 usercopy out 來傳回值。讀取是否執行使用者副本,實際上是實作細節。我們可以先實作執行使用者副本的作業,然後變更 vDSO/核心介面,透過暫存器傳回值,並更新 vDSO 來儲存傳回的值,藉此進行最佳化。
我們預期 Starnix Suspension 用途不會對效能造成重大影響 (改善或回歸)。
就 Timestamped Graphics Fence 用途而言,我們預期 CPU 和記憶體都會有小幅改善。與以 VMO 為基礎的信號燈相比,計數器需要的記憶體較少 (幾十個位元組與超過一頁),建立成本也較低。
人體工學
Counter 的設計用途是搭配現有的非同步等待模式 (例如 Zircon Ports)。
回溯相容性
Counter 是新的 Zircon 物件,因此沒有回溯相容性問題。
安全性考量
沒有安全考量。
隱私權注意事項
無隱私權注意事項。
測試
我們會新增核心測試,測試新的 Zircon 物件。
說明文件
系統會新增/更新系統呼叫文件。
缺點、替代方案和未知事項
自行製作
如上所述,zx::event 不足以用於 Starnix 用途。不過,zx::event 加上共用的整數和議定的通訊協定就足夠了。除了導入新的 Zircon 物件,Starnix Runner 和 Starnix Kernel 也可以使用 zx::event,以及包含整數的共用對應 VMO,並以原子方式操控整數。雖然這種解決方案可行,但較為複雜,而且有些細微之處可能難以掌握。我們認為 zx::counter 是自然的 Zircon 物件,且新增這個物件造成的核心複雜度增加幅度極小。
一般用途和完整性與狹窄和有限性
設計及建構功能更完整的計數器或信號燈物件,與建構僅滿足目前用途的物件之間存在張力。我們過去曾考慮提供類似物件,但由於使用情境不夠具體,因此暫緩推出。這次我們要建構的相對簡單,旨在滿足一或兩個案例。我們的目標是隨著更多用途出現,演進或取代這個物件,而不是建立大量類似但不太相同的物件。
另請參閱「未來發展方向」。
不帶正負號的計數
主要用途只需要未簽署的計數器。我們可以變更行為,讓值在邏輯上為不帶正負號。我們也可以使用選項旗標,指定是否為已簽署或未簽署,並在未簽署時將遞減至零以下視為錯誤。由於我們沒有找到充分的論據來支援有符號和無符號的語意,且有符號的語意更具彈性,可涵蓋更多未來的用途,因此建議只提供有符號的語意。
缺少初始值
我們考慮過替代方案,也就是將初始值傳遞至 create 方法。不過,呼叫端只要在允許控制代碼「逸出」前呼叫 add,即可達到相同目的,因此我們認為這項功能並無必要。
未來發展方向
我們預期在近期到中期內,zx::counter不會廣泛採用。在未來的某個時間點,我們可能會以更一般且完整的信號燈物件,取代 zx::counter (或許也包括 zx::event)。