将io_uring_submit()置于await_suspend中触发liburing错误的原因
核心原因拆解
1. 协程挂起与IO请求生命周期的不匹配
当你在await_suspend()中调用io_uring_submit()时,协程已经处于挂起状态,而awaiter对象通常是协程栈上的局部变量——await_suspend()执行完毕后,awaiter会被立即销毁。如果你的IO请求上下文(比如关联的SQE、完成回调标记)绑定在awaiter上,内核处理请求时会因为找不到有效上下文,直接将请求标记为取消(返回错误码125)。
而把io_uring_submit()移到message_pump()循环中时,awaiter的生命周期会被维持到请求完成(循环会持续运行并等待CQE),内核能正常找到请求对应的上下文,因此不会触发取消错误。
2. 完成事件处理的时序问题
io_uring_submit()只是将SQE提交到内核,但后续需要通过io_uring_wait_cqe()或类似调用收割完成事件(CQE),并唤醒对应的协程。如果在await_suspend()中提交请求后,程序没有立即进入等待CQE的逻辑(比如message_pump()还没启动),会出现两种情况:
- 程序可能直接进入其他流程甚至退出,导致uring实例被销毁,内核检测到资源释放后取消请求;
- 即使程序没退出,长时间未被消费的CQE可能被内核视为无效请求,触发取消逻辑。
而在message_pump()循环中,提交请求后会立即进入等待CQE的逻辑(1秒休眠本质是给内核足够时间处理请求并返回CQE),完成事件能被及时处理,协程也能被正确唤醒,因此功能正常。
3. liburing的提交与等待的耦合逻辑
liburing的设计要求提交请求后,需要有对应的等待/收割逻辑跟进。如果单独调用io_uring_submit()而没有后续的CQE处理,内核无法确认用户态是否还关心请求结果,在某些场景下会主动取消请求。而message_pump()循环的模式符合liburing的常规使用流程:提交→等待→收割→唤醒协程,因此能避免这类错误。
内容的提问来源于stack exchange,提问作者Frederic Schönberger

