非同步與高效能輸入輸出

io_uring(提交環與完成環)

/ io_uring -> EYE-oh YOO-ring /

想像你和廚房不再為每道菜打電話(每點一次一通電話、每取一次一通電話),而是共用兩條輸送帶:你把點菜單放上一條帶子,做好的菜從另一條帶子送回。例行往來誰都不必打電話。io_uring 就是 Linux 以這個概念為基礎打造的現代非同步 I/O 介面——你的程式與核心之間有兩個共享的環形緩衝區,讓你提交 I/O 並收割結果,只需很少、甚至零個系統呼叫。

它如何運作:io_uring 給你兩個位於同時映射進「使用者空間與核心」之記憶體中的佇列——提交佇列(SQ)與完成佇列(CQ)。要做一個操作,你填一個提交佇列項目(SQE)來描述它——把這個 fd 讀進這個緩衝區、在這個通訊端上 accept、寫出這些位元組——然後推進 SQ 的尾端。一次 io_uring_enter() 系統呼叫(或在有核心輪詢執行緒時,完全不需系統呼叫)把這一批交給核心。核心非同步地執行每個操作,做完後張貼一個完成佇列項目(CQE),帶著你的 user_data 標記與結果。因為這些環是共享記憶體,每個系統呼叫批次處理數十個操作是常態,而登記過(事先釘住)的緩衝區與登記過的檔案,讓核心得以省去逐次呼叫的設定。它是完成模型的介面:核心做讀取,然後告訴你它做完了。

為何重要:io_uring 移除了限制 epoll 式設計的「每操作一個系統呼叫」負擔,而且關鍵在於它讓真正的非同步「磁碟」I/O 成為可能(epoll 從來辦不到——一般檔案總回報就緒)。它能把幾乎任何系統呼叫表達成非同步,包括 accept、connect、send、recv,甚至是相依操作的串鏈。誠實的提醒:它只在 Linux、且相對年輕,API 繁複而容易誤用,交給核心的緩衝區必須一直有效到對應的完成抵達為止,而它的強大也使它成為核心安全問題的顯著來源,所以有些強化過的環境會停用它。

struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); io_uring_prep_read(sqe, fd, buf, len, offset); sqe->user_data = 42; io_uring_submit(&ring); struct io_uring_cqe *cqe; io_uring_wait_cqe(&ring, &cqe); /* cqe->res = 讀到的位元組數,cqe->user_data = 42 */

把一個讀取當作 SQE 提交,再以帶著你標記的 CQE 收割結果——中間是核心做了讀取。

io_uring 是完成式的,所以你在 SQE 中傳入的緩衝區必須一直存活且不變,直到其 CQE 抵達為止——太早釋放或重用它,是個核心可能寫入其中的釋放後使用。它龐大的攻擊面已產生真實的 CVE,這也是有些發行版與沙箱會限制它的原因。

又称
Linux ring-based async I/Oshared ring buffersio_uring(共享環形緩衝區)