Qemu AIO:
struct LinuxAioState {
AioContext *aio_context;
/* 在event_notifier_init中初始化,第一个参数是允许的最大的异步IO的个数 */
io_context_t ctx;
/* 这个可以是eventfd或是pipe两种形式。只是对这两种形式的一个简单封装。在event_notifier_init中初始化 */
EventNotifier e;
/* io queue for submit at batch */
LaioQueue io_q;
/* I/O completion processing */
QEMUBH *completion_bh;
struct io_event events[MAX_EVENTS];
int event_idx;
int event_max; /* 把iocb结构挂载这个链表上。 并非每一次IO都要触发submit,aio_do_submi这里只是一个 生产者,而ioq_submit是消费者? */
};
读写数据的调用流程
bdrv_driver_pwritev
------
raw_co_prw
| ------> aio_get_linux_aio
| |
| | ------> laio_init
| | | -----> event_notifier_init(创建eventfd)
| | -----> laio_attach_aio_context (把eventfd加入到epoll中,并把eventfd与qemu_laio_completion_cb关联起来。等下该eventfd有数据了就使用这个回调处理)
|
| -------> laio_co_submit (老版本没有启用AIO,所以不会走到这里,走的是线程池方式)
| |
| | ------> laio_do_submit
| | |
| | | ------> io_prep_pwritev(拼装iocb,用于aio)
| | |
| | | ------> io_set_eventfd(把event fd放到iocb中。等下发给iocb内核,内核做完了以后IO,然后通过eventfd_write,这样用户态便能通过read读取到结果了)
| | |
| | | ------> ioq_submit(最后调用io_submit把多个iocb一起发给内核)
| |
| | ------> qemu_coroutine_yield(为什么这里需要yield呢? 因为laio_do_submit函数的返回值并非此次IO的结果。所以它必须等到内核通知此次IO结果。qemu_laio_process_completion中会填充此次IO的结果,并调用qemu_coroutine_enter重新跳转到这里执行。这里就退到该协程的调用者处。 )
也就是说使用了AIO以后就没有线程池了,只有再flush的时候才会使用到线程池
读写完成的调用流程
qemu_laio_completion_cb
|
| ------> event_notifier_test_and_clear(使用read清除掉eventfd里面的数据)
|
| ------> io_getevents(获取io结果)
|
| ------> qemu_laio_process_completion(处理IO结果)
|
| ------> qemu_coroutine_enter(回到 laio_co_submit处继续执行)
ioq_submit的结果qemu已经在qemu_laio_process_completion中处理了,但并不代表它真正地写入到了物理磁盘,它其实只是写到磁盘的缓存中(打开文件的时候传入的是O_DIRECT;如果是O_SYNC的话就不用sync了)。 因此还需要把数据flush到磁盘中。那能否再打开文件的时候使用O_SYNC/O_DSYNC让它直接写入到磁盘呢。反正已经是异步的了,我们不应该再来一次flush系统调用带来的上下文切换的开销
bdrv_driver_pwritev
| ------> bdrv_co_flush
| | ------> raw_aio_flush
| | |
| | | ------> paio_submit
| | |
| | | ------> thread_pool_submit_aio
| | |
| | | ------> spawn_thread 没有idle thread的时候才会去创建一个新的aio线程,aio线程会调用aio_worker -> handle_aiocb_flush
| | ------> qemu_coroutine_yield (回退到其调用者)
bdrv_co_io_em_complete 重新enter到哪里去了呢?
qemu_bh_schedule机制
http://www.kuqin.com/linux/20120908/330333.html