io_uring设计理念及使用方式总结
io_uring設計理念及使用方式總結
- overview
- 設計目標
- io_uring系統調用
- io_uring_setup
- 特性
- io_uring_enter
- io_uring_register
- liburing
- op code
- feature
- IO interfaces 比較
- spdk+io_uring
- 參考鏈接
overview
io_uring通過使用先進的IO特性,以及內核支持下的各種免拷貝、免context switch特性,成為kernel下一代高性能異步IO接口,不同于libaio,io_uring支持direct和非direct IO。
Fundamentally, io_uring is just ring based communication channel. ---- Jens
IO請求通過submission queue SQ下發到內核中,內核完成IO之后通過completion queue CQ放回IO result。兩個隊列在用戶態和內核態之間通過共享內存的方式溝通,從而免拷貝,每個SQE(submission queue entry)的大小為64byte,正好容納近一個cache line。內核通過memory ordering、fense等技巧保證整個IO鏈路是不出錯且高效的。
設計目標
作者Jens在文章中明確列出了io_uring的設計目標:
io_uring系統調用
io_uring_setup
創建并配置io_uring
#include <linux/io_uring.h> int io_uring_setup(u32 entries, struct io_uring_params *p);通過io_uring_params設置申請uring的參數:
struct io_uring_params {__u32 sq_entries; // 指定分配多少個sqe__u32 cq_entries; // 指定分配多少個cqe__u32 flags; // io_uring各種參數,包括IORING_SETUP_IOPOLL設置用戶態polling,IORING_SETUP_SQPOLL設置內核態polling,IORING_SETUP_SQ_AFF設置內核態polling的綁核等等__u32 sq_thread_cpu; // 內核態綁核__u32 sq_thread_idle; // 內核態polling 如果idle超過sq_thread_idle milliseconds會進入休眠,進入休眠后用戶態進程必須通過調用io_uring_enter設置IORING_SQ_NEED_WAKEUP 來喚醒內核polling線程__u32 features; // 由內核填寫,表明內核支持那些io_uring特性__u32 wq_fd; // 可以指定一個已經存在的io_uring,而不重新創建__u32 resv[3];struct io_sqring_offsets sq_off; // 指定sq的一些特性struct io_cqring_offsets cq_off; };ring創建好之后是以fd的形式呈現的,用戶可以通過mmap的方式訪問特定的ring
#define IORING_OFF_SQ_RING 0ULL #define IORING_OFF_CQ_RING 0x8000000ULL #define IO_RING_OFF_SQES 0x10000000ULL // 通過以上三個flag來mmap對應的三片IOring的區域 // 下面舉例:sq->ring_ptr = mmap(0, sq->ring_sz, PROT_READ | PROT_WRITE,MAP_SHARED | MAP_POPULATE, fd, IORING_OFF_SQ_RING); if (sq->ring_ptr == MAP_FAILED)return -errno;sq->khead = sq->ring_ptr + p->sq_off.head; // p就是之前設置的io_uring_params sq->ktail = sq->ring_ptr + p->sq_off.tail;// sq配置好之后,用戶態進程作為生產者在sq tail追加sqe,kernel作為消費者從head獲取待處理的sqe上述講解的是io_uring系統調用的方法,我們也可以使用上層封裝liburinginclude/liburing.h中的函數進行初始化和下發IO
特性
我們可以通過io_uring_params配置io_uring不同的特性
io_uring_enter
int io_uring_enter(unsigned int fd, unsigned int to_submit, unsigned int min_complete, unsigned int flags, sigset_t sig);在程序向sq,即請求隊列中插入了IO請求后(可以通過io_uring_get_sqe插入),需要通知內核開始處理,這時就需要調用io_uring_enter。參數中的fd是io_uring的fd,to_submit是提交的IO請求數。
min_complete可以用來阻塞等待內核完成特定數量的請求,前提是flags中設置IORING_ENTER_GETEVENTS。這個功能可以單獨調用來等待內核處理完成。需要注意的是由于采用共享內存隊列的方式來同步請求完成情況,因此程序也可以不使用這個接口而是直接判斷cqring的狀態來獲取IO完成情況并處理cqring中的完成事件(使用liburing中的io_uring_peek_cqe)。
io_uring_register
int io_uring_register(unsigned int fd, unsigned int opcode, void *arg, unsigned int nr_args);這個syscall用于支持一些高級的優化用法,主要有兩種模式,opcode分別為:
liburing
op code
IO entry中不同的opcode可指示kernel做不同的事情:
feature
使用io_uring_get_sqe獲取一個新的sqe之后,可以通過sqe->flages設置特性,一些比較重要的特性列述如下:
IO interfaces 比較
| system calls | at least 1 per I/O | 2 per I/O batch | 1 per patch, zero when using SQ submission thread |
| memory copy | yes | yes - SQE & CEQ | zero-copy for SQE&CQE |
| context switches | yes | yes | minimal context switching polling |
| interrupts | Interupt driven | Interupt driven | supports both interrupts and polling I/O |
| Blocking I/O | synchronous | asynchronous | asynchronous |
| buffer I/O | yes | no | yes |
spdk+io_uring
目前spdk已經支持了io_uring,具體代碼可見pdk/module/bdev/uring/bdev_uring.c,由于目前有一些遠程掛載設備不支持IORING_SETUP_IOPOLL特性,spdk為了維護模塊的通用性,目前的spdk實現也沒有啟用IORING_SETUP_IOPOLL特性,當然定制添加的工作量并不大。
使用如下命令可以在spdk中測試io_uring
./scripts/rpc.py -s /var/tmp/spdk.sock bdev_uring_create /dev/nvme0n1 nvme0n1 512 # 創建uring_bdev LD_PRELOAD=/root/spdk_bdev ./fio ./example_config.fio # 使用fio_plugin測試io_uring,需要更改對應的bdev參數配置。參考鏈接
總結
以上是生活随笔為你收集整理的io_uring设计理念及使用方式总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++右值引用和完美转发
- 下一篇: 深度比较Paxos和Raft