- io_uring
- 简介
- 使用
- 系统调用
- liburing
- 样例
- 代码流程
- 实现
- 系统调用
- `io_uring_setup`
- `io_uring_enter`
- `io_uring_register`
- 参考资料
io_uring 是 Linux 在 5.1 版本引入的一套新的异步 IO 实现。相比 Linux 在 2.6 版本引入的 AIO,io_uring 性能强很多,接近 SPDK[1],同时支持 buffer IO
io_uring 的作者 Jens Axboe 是 Linux 内核块层和其他块设备的维护者,同时也是 CFQ、Noop、Deadline 调度器、blktrace 以及 FIO 的作者,对内核块层非常熟悉
使用 系统调用io_uring 只增加了三个 Linux 系统调用分别是 io_uring_setup,io_uring_enter 和 io_uring_register
他们的入口都在 Linux 内核源码的 fs/io_uring.c 文件中
用户程序可以直接利用 syscall(__NR_xxx, ……) 的方式直接调用,使用起来很麻烦
liburing由于直接使用系统调用较为复杂,Jens Axboe 还提供了封装好的用户态库 liburing,简化了 io_uring 的使用,代码位置在 github 上
样例liburing 仓库的 examples/ 目录下提供了几个简单的样例程序:
| 文件 | 功能 | 其他 |
|---|---|---|
| io_uring-test.c | 读取一个文件的全部内容 | - |
| io_uring-cp.c | 复制一个文件的内容到另一个文件 | 利用 user_data 手动处理读写 IO 之间的依赖,读 IO 返回之后才下发写 IO |
| link-cp.c | 复制一个文件的内容到另一个文件 | 同时下发读写,利用 IOSQE_IO_link 保证读写之间的依赖[2] |
| ucontext-cp.c | 复制 n 个文件的内容到另 n 个文件 | 利用 ucontext 进行上下文切换,模拟协程 |
仔细阅读前三个用例,可以看出利用 io_uring 的一般流程如下:
- 利用 open、fstat 等函数来打开文件以及元数据查看等操作
- 因为 io_uring 替换的是读写接口,后续 io_uring 操作的对象是 fd(由 open 函数执行返回的)
- 利用 io_uring_queue_init 初始化 struct io_uring ring 结构体
- 初始化 struct iovec *iovecs 结构体用于存放用户态 buffer 指针和长度
- 通过 io_uring_get_sqe 获取 sqe
- 通过 io_uring_prep_#OP 对 sqe 填充命令,buffer 以及 offset 信息
- 【可选】 通过 io_uring_sqe_set_data 对 sqe 附加 user_data 信息(该信息会在 cqe 中进行返回)
- 通过 io_uring_submit 对整个 ring 的所有 sqe 进行下发
- 通过 io_uring_wait_cqe 或者 io_uring_peek_cqe 来获取 cqe
- io_uring_wait_cqe 会阻塞当前线程直到有一个 cqe 返回
- io_uring_peek_cqe 不会阻塞,如果当前没有 cqe,就会返回错误
- io_uring_cqe_get_data 可以从 cqe 中获取 user_data
- 通过 io_uring_cqe_seen 对当前 cqe 进行清除,避免被二次处理
- 所有 IO 完成后,通过 io_uring_queue_exit 将 ring 销毁
// fs/io_uring.c
static long io_uring_setup(u32 entries, struct io_uring_params __user *params)
{
struct io_uring_params p;
long ret;
int i;
if (copy_from_user(&p, params, sizeof(p)))
return -EFAULT;
for (i = 0; i < ARRAY_SIZE(p.resv); i++) {
if (p.resv[i])
return -EINVAL;
}
if (p.flags & ~(IORING_SETUP_IOPOLL | IORING_SETUP_SQPOLL |
IORING_SETUP_SQ_AFF))
return -EINVAL;
ret = io_uring_create(entries, &p);
if (ret < 0)
return ret;
if (copy_to_user(params, &p, sizeof(p)))
return -EFAULT;
return ret;
}
SYSCALL_DEFINE2(io_uring_setup, u32, entries,
struct io_uring_params __user *, params)
{
return io_uring_setup(entries, params);
}
io_uring_enter
// fs/io_uring.c
SYSCALL_DEFINE6(io_uring_enter, unsigned int, fd, u32, to_submit,
u32, min_complete, u32, flags, const sigset_t __user *, sig,
size_t, sigsz)
{
struct io_ring_ctx *ctx;
long ret = -EBADF;
int submitted = 0;
struct fd f;
if (flags & ~(IORING_ENTER_GETEVENTS | IORING_ENTER_SQ_WAKEUP))
return -EINVAL;
f = fdget(fd);
if (!f.file)
return -EBADF;
ret = -EOPNOTSUPP;
if (f.file->f_op != &io_uring_fops)
goto out_fput;
ret = -ENXIO;
ctx = f.file->private_data;
if (!percpu_ref_tryget(&ctx->refs))
goto out_fput;
ret = 0;
if (ctx->flags & IORING_SETUP_SQPOLL) {
if (flags & IORING_ENTER_SQ_WAKEUP)
wake_up(&ctx->sqo_wait);
submitted = to_submit;
} else if (to_submit) {
to_submit = min(to_submit, ctx->sq_entries);
mutex_lock(&ctx->uring_lock);
submitted = io_ring_submit(ctx, to_submit);
mutex_unlock(&ctx->uring_lock);
if (submitted != to_submit)
goto out;
}
if (flags & IORING_ENTER_GETEVENTS) {
unsigned nr_events = 0;
min_complete = min(min_complete, ctx->cq_entries);
if (ctx->flags & IORING_SETUP_IOPOLL) {
ret = io_iopoll_check(ctx, &nr_events, min_complete);
} else {
ret = io_cqring_wait(ctx, min_complete, sig, sigsz);
}
}
out:
percpu_ref_put(&ctx->refs);
out_fput:
fdput(f);
return submitted ? submitted : ret;
}
io_uring_register
// fs/io_uring.c
SYSCALL_DEFINE4(io_uring_register, unsigned int, fd, unsigned int, opcode,
void __user *, arg, unsigned int, nr_args)
{
struct io_ring_ctx *ctx;
long ret = -EBADF;
struct fd f;
f = fdget(fd);
if (!f.file)
return -EBADF;
ret = -EOPNOTSUPP;
if (f.file->f_op != &io_uring_fops)
goto out_fput;
ctx = f.file->private_data;
mutex_lock(&ctx->uring_lock);
ret = __io_uring_register(ctx, opcode, arg, nr_args);
mutex_unlock(&ctx->uring_lock);
out_fput:
fdput(f);
return ret;
}
参考资料
- 【PDF】官方 pdf
- 【Kernel】io_uring 性能测试
- 【维基百科】Jens Axboe
- 【Kernel】io_uring IOSQE_IO_link



