RFC-0009:邊緣觸發 async_wait

RFC-0009:Edge 觸發了 async_wait
狀態已接受
區域
  • 核心
說明

也就是說,如果指定 ZX_WAIT_ASYNC_EDGE 旗標,則無論初始信號狀態為何,系統都會略過初始檢查,並將信號集新增至 DispatchObject 的興趣清單。在此作業模式中,其中一個信號必須從無效轉換為有效狀態,封包才能在提供的通訊埠上排入佇列 (在過程中可能需要信號才會成為停用狀態)。

問題
  • 45709
變更
作者
審查人員
提交日期 (年/月)2020-10-24
審查日期 (年/月)2020-11-06

摘要

等待在物件上宣告的信號通常會觸發層級觸發,且會在 zx_object_wait_async 開始時進行檢查,如果信號已經啟用,則封包會立即傳送至通訊埠。此 RFC 會將選項新增至 zx_object_wait_asyncZX_WAIT_ASYNC_EDGE,此功能不會執行該初始檢查,因此只會在信號在呼叫後從停用轉為有效時產生封包。

該功能可能是使用 ZX_WAIT_ASYNC_EDGE 呼叫 zx_object_wait_async,但物件上的信號可能已啟用。在此情況下,只有在物件的信號失效後,再宣告封包時,封包才會在通訊埠上排入佇列。事實上,這是 ZX_WAIT_ASYNC_EDGE 的常用方式。

提振精神

Linux 中的 epoll 輪詢機制可用於兩種模式:層級觸發和邊緣觸發。Fuchsia 的等待功能,特別是 zx_object_wait_asynczx_port_wait 已可採用層級觸發的輪詢。不過,邊緣觸發輪詢需要有能力在預期 (透過 I/O) 活動上的物件上等待信號,才會再次處於閒置狀態,並在後續信號轉換時將封包排入佇列。這是 ZX_WAIT_ASYNC_EDGE 的意圖。

設計

實作 zx_object_wait_asyncZX_WAIT_ASYNC_EDGE 標記非常簡單。

目前,如果其中一個信號集已啟用,系統會直接呼叫觀察器的 OnMatch 方法,無需採取進一步動作。否則,如果所有信號集都未啟用,系統就會透過提供的 SignalObserver 將該組合加入 DispatchObject 的興趣清單。

也就是說,如果指定 ZX_WAIT_ASYNC_EDGE 旗標,則無論初始信號狀態為何,系統都會略過初始檢查,並將信號集新增至 DispatchObject 興趣清單。在此作業模式中,其中一個信號必須從無效狀態轉換為有效狀態,封包才能在提供的通訊埠上排入佇列 (可能必須在過程中收到信號才能成為停用狀態)。

在模擬中使用 ZX_WAIT_ASYNC_EDGE,且觸發 EPOLLET 邊緣觸發功能

這項變更的主要用途是使用 EPOLLET 標記啟用邊緣觸發輪詢。Zircon 中的等待與 epoll 中的輪詢不同,後者會使用 epoll_ctl/EPOLL_CTL_ADD 將該檔案描述元新增至 epoll 檔案描述元,並會持續受到監控,直到透過 epoll_ctl/EPOLL_CTL_DEL 移除為止。Zircon 等待作業 (特別是搭配 zx_object_wait_async) 一律是一次性情況,且在信號生效後再次呼叫 zx_object_wait_async,檔案物件必須「重新修復」。

由於 epoll 用途必須重複呼叫 epoll_wait 才能運作 (不必呼叫 epoll_ctl),因此這個對 zx_object_wait_async 的後置呼叫必須發生在 epoll_wait 的某個位置。

針對預設層級觸發的輪詢,在 epoll_waitzx_port_wait 傳回信號檔案物件後,我們無法在傳回之前呼叫 zx_object_wait_async,因為該物件的信號已採取行動,並在通訊埠上產生重複的封包。因此,系統會保留有效層級觸發的檔案描述元清單,並在等待 zx_port_wait 之前進入 epoll_wait 時,在這份清單的檔案描述元中呼叫 zx_object_wait_async

如果是邊緣觸發的輪詢,在 epoll_wait 傳回後,非阻塞的 I/O 應執行,直到傳回 EWOULDBLOCK。屆時,檔案物件的信號將會停用。此時應呼叫 epoll_wait。不過,如果在傳回 EWOULDBLOCKepoll_wait 的 I/O 作業之間,檔案物件的信號變成有效狀態,那麼除非已呼叫 zx_object_wait_async,否則系統會遺失該事件。接在邊緣觸發模式下,您必須呼叫 zx_object_wait_async 以重新啟動檔案物件,然後才在 epoll_wait 傳回「之前」呼叫檔案物件。這時 ZX_WAIT_ASYNC_EDGE 就必須派上用場。您可在 zx_port_wait 傳回和 epoll_wait 傳回之間使用此旗標呼叫 zx_object_wait_async,因為信號此時會處於啟用狀態,但 ZX_WAIT_ASYNC_EDGE 會略過信號是否有效 (此時這些信號仍在作用中),所以系統不會立即在通訊埠上將封包排入佇列。這表示當 I/O 發生在 EWOULDBLOCK 之前,檔案物件已經由 zx_object_wait_async 監控,且在涵蓋範圍沒有落差。

實作

fxr/438521 已實作在 zx_object_wait_async 中新增的 ZX_WAIT_ASYNC_EDGE 選項,其用途已在 fxr/438656 中實作。

效能

只要新增一個額外方法參數,並檢查該參數是否新增至現有程式碼,那麼效能影響就微小。

安全性考量

隱私權注意事項

測試

已新增其他單元測試。

說明文件

說明文件已新增至實作 CL 中的 zx_object_wait_asynczx_object_wait_async

缺點、替代方案和未知

這似乎是實作這項功能最簡單的方式,也是在其他作業系統中實作邊緣觸發輪詢的方式類似。

使用此標記時,請小心不要錯過 I/O 事件。執行 I/O 後和呼叫 zx_object_wait_async 前,信號可能會變成啟用狀態,在此情況下,從無訊號轉換的轉換可能會被遺漏。實際上,ZX_WAIT_ASYNC_EDGE 旗標會立即使用 zx_port_wait 傳回,表示訊號正在物件上。這樣一來,在物件上執行非封鎖 I/O 後,直到信號失效 (通常直到傳回 ZX_ERR_SHOULD_WAIT 為止),zx_port_wait 就能呼叫以等待其他 I/O 就緒。

由於邊緣觸發的 epoll_wait 中的模式具有 EPOLLET 為 (1) epoll_wait (2) 非封鎖 I/O,直到 fd 尚未就緒 (3) epoll_wait 為止,而 ZX_WAIT_ASYNC_EDGE 的替代方案會在每次 I/O 作業中執行檢查,查看檔案描述元是否已透過 epoll_ctl (且已確定等待可準備) 新增至模擬器檔案EPOLLET在極少數情況下,這需要對 zxiofdio 進行大幅修改。

先前的圖片和參考資料

這項變更的目的是模擬 Linux 中的 EPOLLET 標記:

https://man7.org/linux/man-pages/man7/epoll.7.html