當前位置:編程學習大全網 - 編程語言 - 壹篇文章帶妳讀懂 io_uring 的接口與實現

壹篇文章帶妳讀懂 io_uring 的接口與實現

io_uring 是 Linux 提供的壹個異步 I/O 接口。io_uring 在 2019 年加入 Linux 內核,經過了兩年的發展,現在已經變得非常強大。本文基於 Linux 5.12.10 介紹 io_uring 接口。

io_uring 的實現主要在 fs/io_uring.c 中。

io_uring 的實現僅僅使用了三個 syscall:io_uring_setup, io_uring_enter 和 io_uring_register。它們分別用於設置 io_uring 上下文,提交並獲取完成任務,以及註冊內核用戶***享的緩沖區。使用前兩個 syscall 已經足夠使用 io_uring 接口了。

用戶和內核通過提交隊列和完成隊列進行任務的提交和收割。後文中會出現大量的簡寫,在這裏先做壹些介紹。

用戶通過調用 io_uring_setup 1 初始化壹個新的 io_uring 上下文。該函數返回壹個 file descriptor,並將 io_uring 支持的功能、以及各個數據結構在 fd 中的偏移量存入 params。用戶根據偏移量將 fd 映射到內存 (mmap) 後即可獲得壹塊內核用戶***享的內存區域。這塊內存區域中,有 io_uring 的上下文信息:提交隊列信息 (SQ_RING) 和完成隊列信息 (CQ_RING);還有壹塊專門用來存放提交隊列元素的區域 (SQEs)。SQ_RING 中只存儲 SQE 在 SQEs 區域中的序號,CQ_RING 存儲完整的任務完成數據。2

在 Linux 5.12 中,SQE 大小為 64B,CQE 大小為 16B。因此,相同數量的 SQE 和 CQE 所需要的空間不壹樣。初始化 io_uring 時,用戶如果不在 params 中設置 CQ 長度,內核會分配 entries 個 SQE,以及 entries * 2 個 CQE。

io_uring_setup 設計的巧妙之處在於,內核通過壹塊和用戶***享的內存區域進行消息的傳遞。在創建上下文後,任務提交、任務收割等操作都通過這塊***享的內存區域進行,在 IO_SQPOLL 模式下(後文將詳細介紹),可以完全繞過 Linux 的 syscall 機制完成需要內核介入的操作(比如讀寫文件),大大減少了 syscall 切換上下文、刷 TLB 的開銷。

io_uring 可以處理多種 I/O 相關的請求。比如:

下面以 fsync 為例,介紹執行這個操作中可能用到的結構體和函數。

io_op_def io_op_defs[] 數組中定義了 io_uring 支持的操作,以及它在 io_uring 中的壹些參數。3 比如 IORING_OP_FSYNC:

io_uring 中幾乎每個操作都有對應的準備和執行函數。比如 fsync 操作就對應 io_fsync_prep 和 io_fsync函數。

除了 fsync 這種同步(阻塞)操作,內核中還支持壹些異步(非阻塞)調用的操作,比如 Direct I/O 模式下的文件讀寫。對於這些操作,io_uring 中還會有壹個對應的異步準備函數,以 _async 結尾。比如:

這些函數就是 io_uring 對某個 I/O 操作的包裝。

用戶將需要進行的操作寫入 io_uring 的 SQ 中。在 CQ 中,用戶可以收割任務的完成情況。這裏,我們介紹 SQE 和 CQE 的編碼。

include/uapi/linux/io_uring.h 4 中定義了 SQE 和 CQE。SQE 是壹個 64B 大小的結構體,裏面包含了所有操作可能用到的信息。

io_uring_sqe的定義

CQE 是壹個 16B 大小的結構體,包含操作的執行結果。

繼續以 fsync 為例。要在 io_uring 中完成 fsync 操作,用戶需要將 SQE 中的 opcode 設置為 IORING_OP_FSYNC,將 fd 設置為需要同步的文件,並填充 fsync_flags。其他操作也是類似,設置 opcode 並將操作所需要的參數並寫入 SQE 即可。

通常來說,使用 io_uring 的程序都需要用到 64 位的 user_data 來唯壹標識壹個操作 5。user_data 是 SQE 的壹部分。io_uring 執行完某個操作後,會將這個操作的 user_data 和操作的返回值壹起寫入 CQ 中。

相關視頻推薦

io_uring 新起之秀的linux io模式,是如何媲美epoll的

網絡原理tcp/udp,網絡編程epoll/reactor,面試中正經“八股文”

學習地址:/course/417774?flowToken=1013300

需要C/C++ Linux服務器架構師學習資料加qun812855908獲取(資料包括 C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK,ffmpeg 等),免費分享

io_uring 通過環形隊列和用戶交互。

我們的先以用戶提交任務為例,介紹 io_uring 的內核用戶交互方式。用戶提交任務的過程如下:

接下來我們簡要介紹內核獲取任務、內核完成任務、用戶收割任務的過程。

介紹完 io_uring 的用戶態接口後,我們就可以詳細介紹 io_uring 在內核中是如何實現的了。

io_uring 在創建時有兩個選項,對應著 io_uring 處理任務的不同方式:

這些選項的設定會影響之後用戶與 io_uring 交互的方式:

每個 io_uring 都由壹個輕量級的 io-wq6 線程池支持,從而實現 Buffered I/O 的異步執行。對於 Buffered I/O 來說,文件的內容可能在 page cache 裏,也可能需要從盤上讀取。如果文件的內容已經在 page cache 中,這些內容可以直接在 io_uring_enter 的時候讀取到,並在返回用戶態時收割。否則,讀寫操作會在 workqueue 裏執行。

如果沒有在創建 io_uring 時指定 IORING_SETUP_IOPOLL 選項,io_uring 的操作就會放進 io-wq 中執行。

上圖覆蓋了關閉 IOPOLL 模式下,用戶通過 io_uring 執行操作的整個調用流程。用戶提交的 SQE 經過壹系列處理後,會在 io_queue_sqe 中試探著執行壹次。

所有的操作都被提交到內核隊列後,如果用戶設置了 IORING_ENTER_GETEVENTS flag,io_uring_enter 在返回用戶態前會等待指定個數的操作完成。

之後,Linux 隨時會調度 io-wq 的內核線程執行。此時,io_wq_submit_work 函數會不斷用阻塞模式執行用戶指定的操作。某個操作完整執行後,它的返回值就會被寫入 CQ 中。用戶通過 io_uring 上下文中的 CQ 隊尾位置就能知道內核處理好了哪些操作,無需再次調用 io_uring_enter。

通過火焰圖可以觀察到,在關閉 IOPOLL 時,內核會花大量時間處理讀取操作。

創建 io_uring 時指定 IORING_SETUP_IOPOLL 選項即可開啟 I/O 輪詢模式。通常來說,用 O_DIRECT 模式打開的文件支持使用輪詢模式讀寫內容,執行 read / write 操作。

在輪詢模式下,io_uring_enter 只負責把操作提交到內核的文件讀寫隊列中。之後,用戶需要多次調用 io_uring_enter 來輪詢操作是否完成。

在輪詢模式下,io-wq 不會被使用。提交任務時,io_read 直接調用內核的 Direct I/O 接口向設備隊列提交任務。

如果用戶設置了 IORING_ENTER_GETEVENTS flag,在返回用戶態前,io_uring_enter 會通過 io_iopoll_check 調用內核接口輪詢任務是否完成。

通過火焰圖可以看到,io_uring_enter 在提交任務這壹塊只花了壹小部分時間。大部分時間都在輪詢 I/O 操作是否完成。

在實際生產環境中,我們往往會有這樣的需求:往文件中寫入 n 次,然後用 fsync 落盤。在使用 io_uring時,SQ 中的任務不壹定會按順序執行。給操作設定 IO_SQE_LINK 選項,就可以建立任務之間的先後關系。IO_SQE_LINK 之後的第壹個任務壹定在當前任務完成後執行。7

io_uring 內部使用鏈表來管理任務的依賴關系。每壹個操作在經過 io_submit_sqe 的處理後,都會變成壹個 io_kiocb 對象。這個對象有可能會被放入鏈表中。io_submit_sqe 8 會對含有 IO_SQE_LINK 的 SQE 作特殊處理,處理過程如下:

由此看來,SQ 中連續的 IO_SQE_LINK 記錄會按先後關系依次處理。在 io_submit_sqes 結束前,所有的任務都會被提交。因此,如果任務有先後關系,它們必須在同壹個 io_uring_enter syscall 中批量提交。

其他用於控制 io_uring 任務依賴的選項包括 IOSQE_IO_DRAIN 和 IOSQE_IO_HARDLINK,這裏不再展開。

  • 上一篇:計算機類專業排行
  • 下一篇:JAVA初學者,該怎麽學習JAVA?
  • copyright 2024編程學習大全網