User: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).