使用者:pdeantihuman/沙盒liburing
io_uring 是一個新的 Linux IO 接口,2019年1月8日提交補丁,2019年3月8日合併到了 Linux v5.1-rc1,旨在成為 Linux 的異步文件 IO 接口[1],替代 Linux 的舊異步文件 IO 接口 AIO。io_uring 利用兩個循環緩衝區在內核和用戶態程序之間交換數據,一個用來提交IO請求,另一個用來接收IO結果,前者稱為提交隊列(SQ),後者稱為完成隊列(CQ)。
從新接口的名字 io_uring 也可看出,該接口是 「yet another ring buffer「。
程序接口
io_uring 現在的狀態是開發中,接口一直在變化中。
int io_uring_setup(int entries, struct io_uring_params *params);
創建新的 io_uring 實例,entries 表示與該io_uring實例相對應的循環緩衝區應有多大。entries 必須是一個 2 的冪次,大小必須在 1-4096 之間。返回值是一個與 io_uring 實例相對應的文件描述符。 因為提交隊列和完成隊列是用戶態程序與內核共享的,你需要 MMAP 來訪問這兩個隊列。將這兩個
int io_uring_enter(unsigned int fd, unsigned int to_submit, unsigned int min_complete, unsigned int flags, sigset_t sig);
io_uring_enter 用來通知內核收割提交隊列上已經就緒的IO請求。fd 指的是 io_uring 相對應的文件描述符,to_submit 指已經提交了多少個請求,min_complete 指的是等待多少個請求完成後返回。因此,發起IO請求和等待IO完成事件可以用同一個系統調用。
提交隊列排序
提交到提交隊列中的IO請求稱為SQE(Submission Queue Entry)。
SQE 之間默認是獨立的,意思是任何一個 SQE 的執行不影響另一個 SQE 的執行或者排序。因此 SQE 的排序實際上很自由。
io_uring 支持通過同步指令清空請求隊列。方式是開發者可以提交一個必須等待前面所有 SQE 完成後才能開始執行的 SQE,在這個SQE的 flags 屬性設置IOSQE_IO_DRAIN
即可實現。不過注意這會暫停所有後續的IO請求,它創造的流水線空泡可能比想象中大得多。如果你需要經常為了數據完整性使用這類排序操作,應當使用一個單獨的 IO 上下文來避免對無關IO操作的影響。IOSQE_IO_DRAIN
是一個非常重量級的流水線屏障。
SQE 鏈
開發者如果希望兩個SQE之間有依賴關係,io_uring 支持通過SQE鏈的方式表示一個SQE與另一個SQE之間的依賴關係。如果SQE的flags上設置了IOSQE_IO_LINK
,那麼這個SQE就會等待它前面一個SQE完成後才會開始執行。
比如,需要將數據從一個文件讀出寫入緩衝區,然後緩衝區向另一個文件寫入,是一個非常常見的需求,這種時候你就可以創建一個 SQE 鏈。
示例
應用程序通過 MMAP 訪問提交隊列
struct app_sq_ring app_setup_sq_ring(int ring_fd, struct io_uring_params *p)
{
// 应用程序需要一个结构体变量 sring 存放循环缓冲区的指针
struct app_sq_ring sqring;
void *ptr;
ptr = mmap(NULL, p→sq_off.array + p→sq_entries * sizeof(__u32),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
ring_fd, IORING_OFF_SQ_RING);
sring→head = ptr + p→sq_off.head;
sring→tail = ptr + p→sq_off.tail;
sring→ring_mask = ptr + p→sq_off.ring_mask;
sring→ring_entries = ptr + p→sq_off.ring_entries;
sring→flags = ptr + p→sq_off.flags;
sring→dropped = ptr + p→sq_off.dropped;
sring→array = ptr + p→sq_off.array;
return sring;
}
相關條目
參考資料
- ^ io_uring.pdf (PDF).