Linux io_uring multishot accept怎样持续接收连接事件
Linux io_uring 的 multishot accept 不是“把普通 accept 循环写得更快”,而是让一个 accept 请求在仍有效时连续产生多个完成事件。准备阶段调用 io_uring_prep_multishot_accept(),每次连接到达就从 CQE 的 res 取出新 socket fd;真正决定能否继续复用原请求的是 IORING_CQE_F_MORE。如果这个标志消失,就要按终止事件处理,并在需要继续监听时重新提交 accept。
- 普通 accept 适合兼容性优先的事件循环,multishot accept 适合大量短连接或持续入站连接,减少反复准备 SQE 的维护成本。
- 成功 CQE 也必须检查 flags;只有带
IORING_CQE_F_MORE才表示原请求仍在工作。 - direct 变体把连接放入 registered file table,能减少 fd 查表路径,但需要管理槽位,并处理
-ENFILE。
普通 accept、multishot accept 和 direct 该怎么选
三者的核心差异不是监听 socket,而是“一个 SQE 对应几个连接结果”和“新连接放在哪里”。普通 io_uring_prep_accept() 完成一次就结束,下一次连接需要新的 SQE;io_uring_prep_multishot_accept() 提交一次后可以连续得到多个 CQE;io_uring_prep_multishot_accept_direct() 进一步把每个连接放入 fixed file table,并返回动态分配的表索引。
| 方案 | 一次提交的生命周期 | 结果位置 | 适合场景 |
|---|---|---|---|
| 普通 accept | 一个 CQE 后结束 | 进程 fd 表 | 旧内核兼容、逻辑简单 |
| multishot accept | 持续产生 CQE,直到错误、取消或没有 MORE | 进程 fd 表 | 高频新连接、希望减少 re-submit |
| direct multishot | 持续产生 CQE | registered file table | 已有 fixed files 管理和 fd 查表优化需求 |

如果应用要兼容不支持该能力的内核,先保留普通 accept 路径更稳妥。multishot 的价值主要在于少维护一份“完成后立刻再提交 accept”的代码;它不会替你处理新连接的协议握手、限流、fd 上限或业务连接生命周期。
一次提交 multishot accept 的关键写法
下面示例只展示监听请求和 CQE 处理骨架。user_data 用稳定值标识监听请求,成功得到的 fd 交给连接处理逻辑;示例没有传入 addr 和 addrlen,因为 multishot 反复复用同一块地址存储时,后来的连接可能覆盖之前尚未处理的数据。
#include
#include
#include
#include
enum { ACCEPT_UD = 1 };
static int arm_accept(struct io_uring *ring, int listen_fd) {
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
if (sqe == NULL) {
return -ENOSPC; // SQE 不足时由上层决定何时重试。
}
io_uring_prep_multishot_accept(sqe, listen_fd, NULL, NULL, 0);
io_uring_sqe_set_data64(sqe, ACCEPT_UD); // 让完成循环识别这是监听请求。
return 0;
}
static void handle_cqe(struct io_uring *ring, struct io_uring_cqe *cqe,
int listen_fd) {
const unsigned flags = cqe->flags;
const int more = (flags & IORING_CQE_F_MORE) != 0;
if (cqe->res res; // io_uring 直接返回负 errno,不读取 errno。
fprintf(stderr, "accept CQE error: %d\n", err);
io_uring_cqe_seen(ring, cqe);
arm_accept(ring, listen_fd); // 生产代码还应按错误类型退避或退出。
return;
}
int client_fd = cqe->res; // 非 direct 变体返回新连接的进程 fd。
process_client(client_fd); // 业务代码负责限流、读写和最终 close(client_fd)。
io_uring_cqe_seen(ring, cqe);
if (!more) {
arm_accept(ring, listen_fd); // 原请求已终止,监听仍需继续时重新 arm。
}
}
示例中的 process_client 是业务层占位函数,不能把它理解为已经完成了网络读写。若 cqe->res 为负数,错误已经编码在结果里;若结果为正数,仍要在交给业务层后明确谁负责关闭该 fd。
CQE 处理循环要同时看 res 和 flags
判断 multishot 是否持续,不能只看“这次 accept 成功没有”。成功只说明当前连接拿到了 fd;IORING_CQE_F_MORE 才说明后续 CQE 还会由同一个请求产生。没有 MORE 时,当前 CQE 仍然要消费,但原请求已经结束,监听服务若要继续接收就必须重新 arm。

对退出流程也要保留同一判断:先提交针对 user_data 的取消请求,再消费取消请求 CQE 和 multishot 的最终 CQE。最终 CQE 通常带 -ECANCELED,且不会再带 MORE。不要在看到取消请求完成后立刻销毁 ring,仍需按你的退出协议收完相关 CQE。
direct 变体、兼容性和资源边界
io_uring_prep_multishot_accept_direct() 适合已经使用 registered file table 的服务。动态分配槽位时,每个成功 CQE 返回的是表索引;表没有空闲项会得到 -ENFILE,这不是客户端协议错误,而是资源策略需要处理的信号。没有 fixed file table 管理需求时,普通 multishot 反而更容易维护。
multishot accept 的接口能力从 Linux 5.19 起可用,但部署不能只看开发机内核:启动时可以用能力探测或一次小请求确认,并保留普通 accept fallback。内核不认识该请求、SQE 获取失败、监听 fd 被关闭、进程 fd 或 fixed file table 达到上限,都应进入可观测的退化路径,而不是无条件忙循环 re-arm。
| 现象 | 先检查 | 处理方向 |
|---|---|---|
cqe->res | 负 errno、监听 fd 和取消状态 | 按错误分类退出、退避或重新 arm |
| 成功但没有 MORE | 是否发生终止、取消或内核能力边界 | 消费 CQE 后重新提交一次 accept |
direct 返回 -ENFILE | fixed file table 空槽与连接回收 | 回收连接、扩容槽位或降级到普通 fd |
常见问题
multishot accept 还需要每次提交 SQE 吗?
在请求仍带 IORING_CQE_F_MORE 时不需要;当 MORE 消失或请求因错误结束时,如果监听服务仍要工作,就要重新准备并提交 accept。
为什么成功的 accept 也不能跳过 flags 检查?
因为 cqe->res 只描述当前连接结果,flags 描述原 multishot 请求的后续生命周期。忽略 flags 会让服务在请求已终止后误以为它仍会产生事件。
什么时候直接使用 direct multishot?
只有在服务已经围绕 registered file table 设计了槽位、回收和并发访问策略时才值得使用;否则先采用普通 multishot,问题边界更小。
Python importlib.metadata怎样读取已安装包的入口点
- 上一篇
- Python importlib.metadata怎样读取已安装包的入口点
- 下一篇
- Linux fanotify怎样监控目录变化并过滤事件
-
- 文章 · linux | 52分钟前 | Linux timeout 防火墙规则 nftables sets flags timeout 临时封禁
- Linux nftables sets timeout怎样让临时规则自动过期
- 134浏览 收藏
-
- 文章 · linux | 55分钟前 |
- Linux ethtool ring怎样查看网卡收发环大小
- 264浏览 收藏
-
- 文章 · linux | 57分钟前 |
- Linux ip route get 怎样按源地址和网卡确认内核选路
- 171浏览 收藏
-
- 文章 · linux | 59分钟前 | Linux · 服务管理 · Linux 退出码 Restart systemd.service RestartPreventExitStatus
- Linux systemd.service RestartPreventExitStatus 怎样让指定退出码不重启
- 371浏览 收藏
-
- 文章 · linux | 1小时前 |
- Linux systemd.exec OOMScoreAdjust怎样调整服务被 OOM 选择的优先级
- 269浏览 收藏
-
- 文章 · linux | 1小时前 |
- Linux clone3 set_tid怎样为容器初始化进程设置线程 ID
- 119浏览 收藏
-
- 文章 · linux | 1小时前 | Linux 路径安全 openat2 RESOLVE_BENEATH
- Linux openat2 RESOLVE_BENEATH怎样限制路径不能逃逸根目录
- 208浏览 收藏
-
- 文章 · linux | 1小时前 |
- Linux pidfd_send_signal怎样向指定进程安全发送信号
- 423浏览 收藏
-
- 文章 · linux | 1小时前 |
- Linux fanotify怎样监控目录变化并过滤事件
- 484浏览 收藏
-
- 文章 · linux | 1小时前 |
- Linux io_uring registered files怎样减少高频 I/O 的文件描述符开销
- 120浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 888次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 887次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 970次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 955次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 782次使用
-
- 深入了解Golang网络编程Net包的使用
- 2023-01-23 215浏览
-
- 详解golang执行Linuxshell命令完整场景下的使用方法
- 2023-01-07 426浏览
-
- go程序部署到linux上运行的实现方法
- 2023-01-02 387浏览
-
- Linux系统下Go语言开发环境搭建
- 2023-01-07 242浏览
-
- 使用golang获取linux上文件的访问/创建/修改时间
- 2022-12-31 238浏览
