You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

io_uring中recv_multishot结合缓冲区环调用返回-EINVAL的问题求助及实现咨询

io_uring中recv_multishot结合缓冲区环调用返回-EINVAL的问题求助及实现咨询

我最近在尝试用io_uring的recv_multishot结合缓冲区环(buffer ring)实现UDP套接字的批量接收,遇到了一个棘手的问题:单shot版本的recv能正常工作,但multishot版本始终返回-EINVAL错误。我怀疑是缓冲区环的配置出了问题,但对比了不少类似代码片段后还是没找到问题所在。

环境信息

  • Linux内核版本:6.8
  • liburing版本:2.5
  • 系统:Ubuntu 24.04

测试代码

#include <arpa/inet.h>
#include <liburing.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define BUF_BGID 0
#define N_BUFS 128
#define PAGESIZE 4096
#define BUFSIZE 8192
#define PORT 49000

int main(int argc, char *argv[]) {
    struct sockaddr_in addr;
    int sock_fd;
    char* bufs; // All bufs go here
    int i;
    struct io_uring ring;
    struct io_uring_buf_ring* br = NULL;
    struct io_uring_sqe *sqe;
    struct io_uring_cqe* cqe;

    /* Set up socket */
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    inet_aton("0.0.0.0", &addr.sin_addr);

    /* Create UDP socket */
    sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock_fd < 0) {
        fprintf(stderr, "Could not create socket.\n");
        return 1;
    }
    if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        fprintf(stderr, "Could not bind socket.\n");
        return 1;
    }

    /* Create uring */
    if (io_uring_queue_init(N_BUFS, &ring, 0) < 0) {
        fprintf(stderr, "Could not init ring.\n");
        return 1;
    }

    /* Create buffers and buffer ring */
    if (posix_memalign((void**)&bufs, PAGESIZE, BUFSIZE * N_BUFS)) {
        fprintf(stderr, "Could not allocate memory.\n");
        return 1;
    }
    memset((void*)bufs, 0, BUFSIZE * N_BUFS);

    int err = 0;
    br = io_uring_setup_buf_ring(&ring, N_BUFS, BUF_BGID, 0, &err);
    if (!br) {
        fprintf(stderr, "Could not create buffer ring. Error code: %d\n", err);
        return 1;
    }

    io_uring_buf_ring_init(br);
    for (i = 0; i < N_BUFS; i++) {
        io_uring_buf_ring_add(br, bufs + i * BUFSIZE, BUFSIZE, i, io_uring_buf_ring_mask(N_BUFS), i);
    }
    io_uring_buf_ring_advance(br, N_BUFS);

    /* Set up recv SQE */
    sqe = io_uring_get_sqe(&ring);
    io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT);
    sqe->buf_group = BUF_BGID;
    io_uring_prep_recv_multishot(sqe, sock_fd, NULL, 0, 0); /* This doesn't work! */
    /*io_uring_prep_recv(sqe, sock_fd, bufs, BUFSIZE, 0);*/ /* This works! */

    if (io_uring_submit(&ring) < 0) {
        fprintf(stderr, "Failed to submit SQE.\n");
        return 1;
    }

    if (io_uring_wait_cqe(&ring, &cqe) < 0) {
        fprintf(stderr, "Could not get CQE.\n");
        return 1;
    }

    if (cqe->res < 0) {
        fprintf(stderr, "Bad CQE result: %s\n", strerror(-cqe->res));
        return 1;
    }

    fprintf(stderr, "Buffer index: %d\n", cqe->flags >> IORING_CQE_BUFFER_SHIFT);

    /* Cleanup */
    free(bufs);
    io_uring_queue_exit(&ring);
    close(sock_fd);
    return 0;
}

问题核心

  • 单shot的io_uring_prep_recv可以正常接收UDP数据包
  • 切换到io_uring_prep_recv_multishot后,CQE返回-EINVAL错误
  • 已确认socket绑定正常,缓冲区环的内存分配、初始化流程看起来和示例代码一致

问题分析与解决方案

经过查核io_uring内核文档和opcode定义,发现了问题的根源:

IORING_OP_RECV_MULTISHOT仅支持流式套接字(如TCP),不支持数据报套接字(如UDP)。虽然普通的recv系统调用可以兼容UDP套接字,但multishot版本的RECV opcode被内核限制为仅流式使用,因此传入UDP套接字时会返回-EINVAL。

对于UDP这类数据报套接字,应该使用**IORING_OP_RECVFROM_MULTISHOT**对应的io_uring_prep_recvfrom_multishot函数。即使你不需要获取源地址信息,也需要使用这个opcode,同时可以传入NULL地址参数来忽略源地址。

修改后的SQE设置代码如下:

/* Set up recvfrom_multishot SQE */
sqe = io_uring_get_sqe(&ring);
io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT);
sqe->buf_group = BUF_BGID;
// 使用recvfrom_multishot,传入NULL忽略源地址
io_uring_prep_recvfrom_multishot(sqe, sock_fd, NULL, 0, 0, NULL, NULL);

另外补充两个小建议:

  1. 所有系统调用(socket、bind、io_uring_setup_buf_ring等)都应该添加错误检查,比如io_uring_setup_buf_ring返回NULL时,打印err变量的值可以帮助快速定位问题
  2. 程序退出时记得释放内存、关闭文件描述符和销毁io_uring环(已在修改后的代码中补充)

修改后重新编译运行,multishot接收应该就能正常工作了。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 10:43:06