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

如何在单线程事件循环中同时等待epoll和io_uring的就绪事件

解决方案

核心思路

你不需要同时阻塞在两个系统调用上,只需要将其中一个事件源的通知挂载到另一个的监听队列,始终只阻塞在单个系统调用即可,两种成熟实现方案如下:

方案1:将io_uring事件挂载到epoll监听(对原有epoll逻辑改动最小)

实现步骤

  • 调用eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)创建非阻塞的事件通知fd
  • 调用io_uring_register_eventfd(uring, event_fd)将该fd注册到io_uring,只要io_uring产生完成队列事件(CQE),内核就会向eventfd写入计数,触发可读事件
  • 将eventfd添加到原有epoll的监听集合,监听EPOLLIN事件

事件循环代码示例

while(keep_running) {
    // 仅阻塞在epoll_wait,任意事件源有事件都会立即唤醒
    int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
    for (int i = 0; i < nfds; i++) {
        if (events[i].data.fd == event_fd) {
            // 清空eventfd计数,避免重复触发
            uint64_t cnt;
            while (read(event_fd, &cnt, sizeof(cnt)) > 0);
            // 批量拉取所有io_uring完成事件处理
            struct io_uring_cqe *cqes[MAX_CQES];
            int cqe_cnt = io_uring_peek_batch_cqe(uring, cqes, MAX_CQES);
            for (int j = 0; j < cqe_cnt; j++) {
                // 处理io_uring任务逻辑
                ...
                io_uring_cqe_seen(uring, cqes[j]);
            }
        } else {
            // 原有epoll事件(如数据库驱动socket事件)处理逻辑
            ...
        }
    }
}

优化建议

可以通过io_uring_register_eventfd_enabled接口动态开关eventfd通知,当没有io_uring任务提交时关闭通知,减少不必要的触发开销。


方案2:将epoll挂载到io_uring监听(适合后续向io_uring迁移的场景)

实现步骤

  • 向io_uring提交一个多轮(multishot)poll请求,监听epoll fd的EPOLLIN事件,设置专属标记位区分该事件是epoll触发
  • 仅阻塞在io_uring的等待接口,任意事件源产生事件都会返回CQE

事件循环代码示例

// 初始化阶段提交epoll的multishot poll请求
#define EPOLL_EVENT_MAGIC 0xdeadbeef
struct io_uring_sqe *sqe = io_uring_get_sqe(uring);
io_uring_prep_poll_multishot(sqe, epoll_fd, EPOLLIN);
io_uring_sqe_set_data64(sqe, EPOLL_EVENT_MAGIC);
io_uring_submit(uring);

while(keep_running) {
    struct io_uring_cqe *cqe;
    int ret = io_uring_wait_cqe(uring, &cqe);
    if (ret < 0) continue;

    if (io_uring_cqe_get_data64(cqe) == EPOLL_EVENT_MAGIC) {
        // epoll有事件触发,非阻塞拉取所有epoll事件处理
        int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, 0);
        for (int i = 0; i < nfds; i++) {
            // 原有epoll事件处理逻辑
            ...
        }
        io_uring_cqe_seen(uring, cqe);
    } else {
        // 处理io_uring自身任务完成逻辑
        ...
        io_uring_cqe_seen(uring, cqe);
    }
}

注意事项

  • multishot poll需要内核版本5.15及以上支持,低版本内核可以改用普通poll请求,每次事件触发后重新提交即可
  • 该方案对io_uring侧的事件处理延迟更低,适合io_uring任务占比更高的场景

内容的提问来源于stack exchange,提问作者Mary Chang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 18:06:08