Skip to the content.

FDMonOps

关联的文件:

添加新的需要监听的 fd

在操作的时候,需要 QemuLockCnt list_lock; 来保护,防止

main loop 下的两个模式

main loop thread 中总是用的 qemu_poll_ns 来监听:

ctx->epollfd_tag = g_source_add_unix_fd(&ctx->source,
                         ctx->epollfd,
                         G_IO_IN);

poll 模式,poll 中直接监听所有的 fd:

qemu_aio_context source
├── fd1
├── fd2
├── fd3
└── ...

epoll 或者 io_uring 模式,poll 来监听一个 epollfd / io_uring_fd ,所有的 fd 都被 epollfd / io_uring_fd 来监听

qemu_aio_context source
└── epollfd
    ├── fd1
    ├── fd2
    ├── fd3
    └── ...

iothread 不存在这个需求

FDMonOps::wait 注册的三个 hook ,这是只有 iothread 才会调用的

实现细节

/*
 * These thread-local variables are used only in fdmon_poll_wait() around the
 * call to the poll() system call.  In particular they are not used while
 * aio_poll is performing callbacks, which makes it much easier to think about
 * reentrancy!
 *
 * Stack-allocated arrays would be perfect but they have size limitations;
 * heap allocation is expensive enough that we want to reuse arrays across
 * calls to aio_poll().  And because poll() has to be called without holding
 * any lock, the arrays cannot be stored in AioContext.  Thread-local data
 * has none of the disadvantages of these three options.
 */
static __thread GPollFD *pollfds;
static __thread AioHandler **nodes;
static __thread unsigned npfd, nalloc;
static __thread Notifier pollfds_cleanup_notifier;

static void fdmon_poll_update(AioContext *ctx,
                              AioHandler *old_node,
                              AioHandler *new_node)
{
    /* Do nothing, AioHandler already contains the state we'll need */
}

qemu 中如何处理 epoll 返回 EINTR 的

  1. AIO epoll/poll 主路径

aio_poll() 调用 ctx->fdmon_ops->wait(…) 后不看返回值,只继续 dispatch BH/timer/ready handler。

也就是根本就不关心这个错误。

  1. 主循环 main_loop : main_loop_wait 中不去区分,设置 MAIN_LOOP_POLL_ERR

  2. io_uring_submit() 中才特殊处理:

    /*
     * Loop to handle signals in both cases:
     * 1. If no SQEs were submitted, then -EINTR is returned.
     * 2. If SQEs were submitted then the number of SQEs submitted is returned
     *    rather than -EINTR.
     */
    do {
        ret = io_uring_submit_and_wait(&ctx->fdmon_io_uring, wait_nr);
    } while (ret == -EINTR ||
             (ret >= 0 && wait_nr > io_uring_cq_ready(&ctx->fdmon_io_uring)));

    assert(ret >= 0);

其他情况,QEMU 有通用宏 include/qemu/osdep.h RETRY_ON_EINTR(expr)

iouring 的改动

2025 年修改已经不只是为了 fd 监控性能,而是为了让每个 AioContext 都提供通用的:

aio_add_sqe()

这样块层、FUSE 等模块可以复用 AioContext 自带的 io_uring,而不必各自创建 ring。补丁系列说明 (https://lists.gnu.org/archive/html/qemu-block/2025-03/msg00432.html)

随后:

047dabef97bd block/io_uring: use aio_add_sqe()

把磁盘 aio=io_uring 和 fdmon io_uring 合并到同一个 ring,收益是:

这个其实改动其实有意思:

  1. 如果是两个 iouring 队列
    • iouring 监听 —-> 磁盘文件 iouring 的 fd
  2. 如果一个 iouring 队列
    • iouring 监听所有的 fd 以及提交的所有的 io
      • 这个只能是 io uring 给io handler 做,因为只有

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。