rk3588/kernel-6.1 io_uring 模块架构与实现原理
1. 模块定位与编译入口
io_uring 是 Linux 6.1 中高性能异步 I/O 框架,核心思想是:
- 用户态与内核态共享 SQ/CQ Ring 内存,减少系统调用与拷贝成本;
- 通过统一 SQE opcode 抽象,覆盖文件、网络、超时、poll、资源注册等多类异步操作;
- 用
task_work、io-wq、SQPOLL、IOPOLL等机制平衡时延与吞吐。
在本内核树中:
- 配置入口:
init/Kconfig中config IO_URING(默认y,并select IO_WQ); IO_WQ定义位于fs/Kconfig;- 构建入口:
io_uring/Makefile,由io_uring.o+opdef.o+ 各 opcode 子模块(rw.c、net.c、timeout.c、poll.c等)组成。
2. 目录分层(按职责)
io_uring/ 目录可分为 5 层:
核心控制层
io_uring.c:syscall 入口、ring 创建/销毁、提交流程、完成队列提交、等待逻辑。io_uring.h:核心内联与跨文件函数声明。include/linux/io_uring_types.h:核心结构定义(io_ring_ctx、io_kiocb、io_rings)。
opcode 分发表与调度层
opdef.c/.h:io_op_defs[],定义每个 opcode 的prep/issue/cleanup/fail能力与约束。
执行后端层
io-wq.c/.h:内核 worker 池(阻塞/慢路径异步执行)。sqpoll.c/.h:SQ 轮询线程(减少用户提交 syscall)。poll.c:fast poll 与轮询唤醒链路。
功能子模块层(按 opcode 家族)
- 文件/元数据:
rw.c、fs.c、openclose.c、statx.c、xattr.c、sync.c、splice.c、advise.c - 网络:
net.c、msg_ring.c、uring_cmd.c - 时间与取消:
timeout.c、cancel.c - 资源管理:
rsrc.c、kbuf.c、filetable.c、tctx.c - 观测支持:
fdinfo.c、notif.c
- 文件/元数据:
共享工具层
alloc_cache.h、refs.h、slist.h等通用基础设施。
3. 核心数据结构
3.1 struct io_rings(用户态共享 ring 元数据)
- 包含
sq.head/tail、cq.head/tail、mask、entries、sq_dropped、cq_overflow、cqes[]。 - 职责边界清晰:用户写 SQ tail / 读 CQ tail,内核写 SQ head / CQ tail。
3.2 struct io_ring_ctx(每个 ring fd 一个上下文)
- 提交面:
uring_lock、sq_array、sq_sqes、submit_state、固定文件/缓冲资源。 - 完成面:
completion_lock、CQ 缓存指针、cq_wait、overflow list。 - 异步面:
iopoll_list、work_llist、sq_data(SQPOLL)、io_wq关联。 - 资源面:
file_table、user_bufs、rsrc_node、personality、restrictions。
3.3 struct io_kiocb(单请求对象)
- 绑定 opcode 与请求态:
opcode、flags、cqe、file、task、link。 - 支持多态命令区:
cmd.data[56]供各子模块覆盖使用。 - 维护生命周期与异步状态:
refs、poll_refs、async_data、io_task_work、work。
3.4 io_op_defs[](操作语义表)
- 每个 opcode 定义:
- 是否需要文件、是否支持 iopoll、是否支持 buffer select;
prep()参数解析;issue()执行;cleanup()/fail()兜底路径。
- 使 opcode 扩展呈“声明式分发表 + 独立模块实现”结构。
4. 三条主调用链
4.1 创建链:io_uring_setup -> io_uring_create
SYSCALL_DEFINE2(io_uring_setup)进入;- 校验
io_uring_params.flags; io_uring_create():- 计算并规范化
sq_entries/cq_entries(默认cq=2*sq); io_ring_ctx_alloc()创建上下文;io_allocate_scq_urings()分配共享 ring 与 SQE 区;io_sq_offload_create()建立 SQPOLL/附加 WQ;- 初始化
sq_off/cq_off/features返回给用户态; anon_inode_getfile_secure()创建 ring file;io_uring_install_fd()安装 fd。
- 计算并规范化
实现要点:通过一个匿名 fd 对外暴露 ring,用户再通过 mmap 映射 SQ/CQ 与 SQE 区。
4.2 提交链:io_uring_enter -> io_submit_sqes
SYSCALL_DEFINE6(io_uring_enter)获取 ctx;- 非 SQPOLL 场景下加
uring_lock并进入io_submit_sqes(ctx, to_submit); io_get_sqe()从共享 SQ 拉取一个 SQE(用READ_ONCE保证稳定读);io_submit_sqe():io_init_req()解析 SQE(flags/限制/文件/cred/link/drain 等);- 根据 link / force_async 决定 inline issue 或 fallback 到异步;
io_queue_sqe()->io_issue_sqe():io_op_defs[opcode].issue()分发到具体模块;- 成功后进入完成流程,失败走 async/poll/iowq 兜底;
- 批处理结束
io_commit_sqring()推进sq.head。
实现要点:
- 支持
IORING_SETUP_SUBMIT_ALL:部分 SQE 出错不立即中断整批; - 支持 link/hardlink/drain:表达跨请求依赖;
- 支持 personality:按请求临时切换 cred。
4.3 完成链:io_req_complete_post -> CQ ring
- 请求完成后写入
req->cqe; __io_fill_cqe_req()获取 CQE 槽(优先走缓存cqe_cached);io_commit_cqring()以 release 语义更新cq.tail;io_cqring_wake()唤醒cq_wait上等待线程;- 如果 CQ 满,进入 overflow 路径并更新
cq_overflow/check_cq。
实现要点:
- CQ 写入与 tail 更新严格内存序;
- 通过 overflow + dropped 标志向用户态传达背压与丢失信息。
5. 执行模型(四种路径)
5.1 inline(最短路径)
- 提交线程直接
issue(),适用于可立即完成或非阻塞成功的请求。
5.2 task_work defer
- 将完成或后续动作挂到 task_work,在返回用户态前/合适时机运行。
IORING_SETUP_DEFER_TASKRUN可将本地 work 延后到提交者线程统一处理。
5.3 io-wq worker pool(阻塞慢路径)
io-wq.c提供每 NUMA 节点io_wqe池;- 分 bounded/unbounded 两类账户;
- 对可能阻塞的请求转交 worker 执行,避免卡住提交线程。
5.4 SQPOLL / IOPOLL(轮询路径)
SQPOLL:专用io_sq_thread()轮询 SQ,减少提交 syscall;IOPOLL:适用于支持轮询完成的设备 I/O,请求挂入iopoll_list并由轮询收割。
6. 资源管理机制
6.1 固定文件与固定缓冲
rsrc.c+filetable.c+kbuf.c管理注册资源;- 固定资源减少每请求
fget/pin成本,提升高 QPS 稳定性。
6.2 内存记账与限制
__io_account_mem()与RLIMIT_MEMLOCK绑定;- 同步维护
user->locked_vm与mm->pinned_vm,防止无限 pin 页。
6.3 资源异步释放
io_rsrc_put_work()通过 workqueue 回收;- 可选给 tag 发送辅助 CQE,通知用户资源释放完成。
7. 并发与内存序设计
关键并发策略:
- 提交主路径由
uring_lock串行化; - 完成面由
completion_lock+ CQ 缓存批量化降低锁开销; - ring head/tail 更新使用
smp_store_release/smp_load_acquire; - 用户共享内存读取关键字段使用
READ_ONCE,避免被用户态并发改写导致重读不一致。
核心目标:在“用户共享内存 + 内核并发执行”模型下保证可见性与一致性。
8. opcode 扩展方法(实现视角)
新增一个 opcode 的标准步骤:
- 新增
xxx.c/.h,实现prep/issue/(cleanup); - 在
opdef.c的io_op_defs[]填入能力位与函数指针; - 在
Makefile加入目标文件; - 若需要 register 能力、资源或 fdinfo 展示,再接入
io_uring_register/fdinfo路径。
这套模式让核心调度保持稳定,具体语义在子模块独立演进。
9. 常见问题定位建议
提交了但没完成
- 看
sq.head/tail与cq.head/tail是否推进; - 检查
cq_overflow、sq_dropped是否增长; - 核查
IORING_SETUP_DEFER_TASKRUN下 task_work 是否被及时运行。
- 看
高负载下延迟抖动
- 检查是否频繁 fallback 到
io-wq(说明 inline 非阻塞命中率低); - 评估固定文件/固定缓冲是否启用;
- 观察 SQPOLL/IOPOLL 与实际设备能力是否匹配。
- 检查是否频繁 fallback 到
资源注册失败或 ENOMEM
- 关注
RLIMIT_MEMLOCK、pinned_vm、固定缓冲大小; - 检查注册参数和索引范围是否越界。
- 关注
链式请求行为异常
- 核对
IOSQE_IO_LINK、IOSQE_IO_HARDLINK、IOSQE_IO_DRAIN组合; - 关注失败后
REQ_F_FAIL与 link 断链策略。
- 核对
10. 一句话总结
io_uring 在该 6.1 内核中的架构本质是:
“共享 ring + opcode 分发表 + 多后端执行引擎(inline/task_work/io-wq/sqpoll)+ 资源注册体系”,通过细粒度并发控制与内存序保证,实现高吞吐、低 syscall 成本的统一异步 I/O 平台。
正在加载留言…