io_uring的fixed buffers是预先注册页对齐并锁定的用户态内存,供I/O操作直接复用,从而避免每次调用的copy_to_user/copy_from_user拷贝;需用memalign和mlock分配锁定,通过io_uring_register_buffers注册iovec数组,提交时设IOSQE_BUFFER_SELECT标志并指定buf_index。

什么是 io_uring 的 fixed buffers,为什么值得配
固定缓冲区(fixed buffers)是 io_uring 提供的零拷贝机制之一:你提前注册一批用户态内存页,后续所有 read、write、recv、send 等操作可直接复用这些内存,内核不再做 copy_to_user/copy_from_user。前提是这些内存必须满足:对齐到页边界(通常 4KiB)、锁定(mlock)、且注册进 ring。
不配 fixed buffers 时,每次提交 IORING_OP_READ 都得传一个用户地址,内核会临时拷贝数据;配了之后,只需传 buffer ID(buf_index),内核直接往你注册好的页里读/写。
如何用 io_uring_register_buffers 注册固定缓冲区
注册前必须确保内存满足三条件:页对齐、足够大、已 mlock 锁定。否则 io_uring_register_buffers 会失败并返回 -EINVAL 或 -ENOMEM。
void *bufs = memalign(4096, 2 * 4096); // 分配 2 个页,对齐
if (!bufs) { /* handle error */ }
if (mlock(bufs, 2 * 4096)) { /* handle error */ }
<p>struct iovec iov[2] = {
{.iov_base = bufs, .iov_len = 4096},
{.iov_base = (char*)bufs + 4096, .iov_len = 4096},
};
int ret = io_uring_register_buffers(&ring, iov, 2);
if (ret < 0) {
fprintf(stderr, "register buffers failed: %s\n", strerror(-ret));
}</p>-
iov数组长度不能超过IORING_MAX_BUFFERS(当前通常是 1024) - 每个
iov_len建议是页大小整数倍,避免跨页边界访问引发 fault - 注册后不能
free或munmap这些内存,否则触发 page fault 或 kernel panic
提交请求时怎么用 buffer ID 而不是地址
注册成功后,每个 iovec 对应一个从 0 开始的 buffer ID。提交 IORING_OP_READ 时:
立即学习“C++免费学习笔记(深入)”;
- 清零
sqe->addr(不能再填用户地址) - 设置
sqe->rw_flags |= IOSQE_BUFFER_SELECT - 设置
sqe->buf_index = 0(选第一个 buffer)
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); io_uring_prep_read(sqe, fd, nullptr, len, offset); sqe->rw_flags |= IOSQE_BUFFER_SELECT; sqe->buf_index = 0; // 使用第 0 个注册 buffer
-
buf_index超出注册数量会返回-ENOBUFS(在 CQE 中体现为res = -ENOBUFS) - 同一 buffer 可被多个并发请求复用,但需你自己保证读写互斥(比如用不同 offset,或加应用层锁)
- 如果用
IORING_OP_RECV,同样适用;但IORING_OP_ACCEPT、IORING_OP_CONNECT不支持 fixed buffers
常见踩坑点和调试线索
-
io_uring_register_buffers 失败最常见原因是没 mlock:检查 /proc/PID/status 中 MMUPageSize 和 RssAnon,确认页是否真被锁住
- 提交后 CQE 返回
res = -ENOBUFS:说明 buf_index 越界,或注册时传的 iovec 数量与实际不符
- 用
strace 看不到 read/write 系统调用 —— 正常,fixed buffers 下这些操作由内核在上下文切换时直接完成,不走 syscall 入口
- buffer 内容看起来“旧”或“乱”:大概率是你没清空 buffer,或多个请求写同一区域没同步;fixed buffers 不自动清零,也不做线程安全保护
io_uring_register_buffers 失败最常见原因是没 mlock:检查 /proc/PID/status 中 MMUPageSize 和 RssAnon,确认页是否真被锁住res = -ENOBUFS:说明 buf_index 越界,或注册时传的 iovec 数量与实际不符strace 看不到 read/write 系统调用 —— 正常,fixed buffers 下这些操作由内核在上下文切换时直接完成,不走 syscall 入口buffer ID 是纯索引,没有生命周期管理;注销要调 io_uring_unregister_buffers,且必须在 ring 销毁前调,否则泄漏。


















