io_uring 入门

io_uring 是 Linux 内核 5.1 版本引入的异步 I/O 框架,

#原理

#代码组织

完整的 io-uring 由内核 syscall、liburing 库、用户态程序三部分组成。

  • 内核提供了 io_uring_register, io_uring_setup, io_uring_enter 三个 syscall 接口。
  • liburing 库提供了 io_uring_queue_init, io_uring_get_sqe, io_uring_wait_cqe 等函数。
  • 用户态程序通过 liburing 库调用内核 syscall 接口,实现 I/O 请求的提交和完成。

#核心结构

每个 io_uring 实例由一个 struct io_uring 结构体表示。

1
2
3
4
5
6
7
8
9
10
11
12
struct io_uring {
struct io_uring_sq sq;
struct io_uring_cq cq;
unsigned flags;
int ring_fd;

unsigned features;
int enter_ring_fd;
__u8 int_flags;
__u8 pad[3];
unsigned pad2;
};

每个 io_uring 实例包含两个环形队列:

  • submission queue (SQ):提交队列,用于提交 I/O 请求。
  • completion queue (CQ):完成队列,用于接收 I/O 请求的完成结果。

#使用方式

应用层通过 io_uring_queue_init 初始化一个 io_uring 实例。

1
2
3
4
5
6
struct io_uring ring;
struct io_uring_params params;
memset(&params, 0, sizeof(params));
params.flags |= IORING_SETUP_SQPOLL; // 开启 SQPOLL 模式
params.sq_thread_idle = 2000; // 2000ms 内没有 I/O 请求,内核线程会睡眠
io_uring_queue_init(1024, &ring, &params);

应用层通过 io_uring_get_sqe 创建 SQE(Submission Queue Entry) 结构体,并设置 I/O 请求的参数: 操作类型、文件描述符、缓冲区、偏移量等。可以创建多个 SQE 结构体,并设置不同的私有数据。

1
2
3
4
5
6
7
8
9
10
11
struct io_uring_sqe *sqe;
sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, fd, buf, len, offset); // 提交一个 read 请求
io_uring_sqe_set_data(sqe, (void *)1); // 设置 SQE 的私有数据

struct io_uring_sqe *sqe2;
sqe2 = io_uring_get_sqe(&ring);
io_uring_prep_fsync(sqe2, 1, 0); // 提交一个 fsync 请求
io_uring_sqe_set_data(sqe2, (void *)2); // 设置 SQE 的私有数据

io_uring_submit(&ring);

提交后,应用层通过 io_uring_wait_cqe 等待已完成的 I/O 请求,并获取完成结果。

1
2
3
4
5
6
7
8
9
struct io_uring_cqe *cqe;
int ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
printf("io_uring_wait_cqe failed: %s\n", strerror(-ret));
return -1;
}

io_uring_cqe_get_data(cqe); // 获取完成结果
io_uring_cqe_seen(&ring, cqe); // 标记完成结果已处理

#工作模式

  • 中断模式 (默认): 在 io_uring_enter 时,采用中断模式,等待 CQ 中出现完成的 IO 请求。
  • IORING_SETUP_IOPOLLio_uring_enter 时,采用 polling 而非中断模式,等待 CQ 中出现完成的 IO 请求。
  • IORING_SETUP_SQPOLL 创建一个专用的内核线程 polling SQ 并处理 IO。
  • IORING_SETUP_IOPOLL | IORING_SETUP_SQPOLL 睡眠等待 CQE 完成。