RFC-0009:Edge 觸發了 async_wait | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 也就是說,如果指定 ZX_WAIT_ASYNC_EDGE 旗標,則無論初始信號狀態為何,系統都會略過初始檢查,並將信號集新增至 DispatchObject 的興趣清單。在此作業模式中,其中一個信號必須從無效轉換為有效狀態,封包才能在提供的通訊埠上排入佇列 (在過程中可能需要信號才會成為停用狀態)。 |
問題 | |
變更 | |
作者 | |
審查人員 | |
提交日期 (年/月) | 2020-10-24 |
審查日期 (年/月) | 2020-11-06 |
摘要
等待在物件上宣告的信號通常會觸發層級觸發,且會在 zx_object_wait_async
開始時進行檢查,如果信號已經啟用,則封包會立即傳送至通訊埠。此 RFC 會將選項新增至 zx_object_wait_async
、ZX_WAIT_ASYNC_EDGE
,此功能不會執行該初始檢查,因此只會在信號在呼叫後從停用轉為有效時產生封包。
該功能可能是使用 ZX_WAIT_ASYNC_EDGE
呼叫 zx_object_wait_async
,但物件上的信號可能已啟用。在此情況下,只有在物件的信號失效後,再宣告封包時,封包才會在通訊埠上排入佇列。事實上,這是 ZX_WAIT_ASYNC_EDGE
的常用方式。
提振精神
Linux 中的 epoll
輪詢機制可用於兩種模式:層級觸發和邊緣觸發。Fuchsia 的等待功能,特別是 zx_object_wait_async
和 zx_port_wait
已可採用層級觸發的輪詢。不過,邊緣觸發輪詢需要有能力在預期 (透過 I/O) 活動上的物件上等待信號,才會再次處於閒置狀態,並在後續信號轉換時將封包排入佇列。這是 ZX_WAIT_ASYNC_EDGE
的意圖。
設計
實作 zx_object_wait_async
的 ZX_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_wait
中 zx_port_wait
傳回信號檔案物件後,我們無法在傳回之前呼叫 zx_object_wait_async
,因為該物件的信號已採取行動,並在通訊埠上產生重複的封包。因此,系統會保留有效層級觸發的檔案描述元清單,並在等待 zx_port_wait
之前進入 epoll_wait
時,在這份清單的檔案描述元中呼叫 zx_object_wait_async
。
如果是邊緣觸發的輪詢,在 epoll_wait
傳回後,非阻塞的 I/O 應執行,直到傳回 EWOULDBLOCK
。屆時,檔案物件的信號將會停用。此時應呼叫 epoll_wait
。不過,如果在傳回 EWOULDBLOCK
和 epoll_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_async
。zx_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
在極少數情況下,這需要對 zxio
和 fdio
進行大幅修改。
先前的圖片和參考資料
這項變更的目的是模擬 Linux 中的 EPOLLET 標記:
https://man7.org/linux/man-pages/man7/epoll.7.html