zoukankan      html  css  js  c++  java
  • QEMU IO事件处理框架

    Qemu IO事件处理框架

    qemu是基于事件驱动的,在基于KVM的qemu模型中,每一个VCPU对应一个qemu线程,且qemu主线程负责各种事件的监听,这里有一个小的IO监听框架,本节对此进行介绍。

    1.1    涉及结构

    struct GArray {
    
      gchar *data;
    
      guint len;
    
    };

    Data指向一个GpollFD数组,len表示数组的个数。

    struct GPollFD
    
    {
    
      gint          fd;
    
      gushort       events;
    
      gushort       revents;
    
    };

    Fd为监听的fd,event为请求监听的事件,是一组bit的组合。Revents为poll收到的事件,根据此判定当前什么事件可用。

    typedef struct IOHandlerRecord {
    
        IOCanReadHandler *fd_read_poll;
    
        IOHandler *fd_read;
    
        IOHandler *fd_write;
    
        void *opaque;
    
        QLIST_ENTRY(IOHandlerRecord) next;
    
        int fd;
    
        int pollfds_idx;
    
        bool deleted;
    
    } IOHandlerRecord;

    该结构是qemu中IO框架的处理单位,fd_read和fd_write为注册的对应处理函数。Next表示该结构会连接在一个全局的链表上,fd是对应的fd,delete标志是否需要从链表中删除该结构。

    1.2    代码分析

    1.2.1         初始化阶段

    Main-> qemu_init_main_loop

    Main-> main_loop-> main_loop_wait

    qemu_init_main_loop

    int qemu_init_main_loop(void)
    
    {
    
        int ret;
    
        GSource *src;
    
        init_clocks();
    
        ret = qemu_signal_init();
    
        if (ret) {
    
            return ret;
    
        }
    
           //malloc a globle fd array
    
        gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
    
           //create a aio context
    
        qemu_aio_context = aio_context_new();
    
           //get event source from aio context
    
        src = aio_get_g_source(qemu_aio_context);
    
           //add source to main loop
    
        g_source_attach(src, NULL);
    
        g_source_unref(src);
    
        return 0;
    
    }

    Qemu中的main loop主要采用 了glib中的事件循环,关于此详细内容,准备后面专门写一小节,本节主要看主体IO框架。

           该函数主要就分配了一个Garray结构存储全局的GpollFD,在main_loop中的main_loop_wait阶段有两个比较重要的函数:qemu_iohandler_fill,os_host_main_loop_wait和qemu_iohandler_poll,前者把用户添加的fd信息注册到刚才分配的Garray结构中,os_host_main_loop_wait对事件进行监听,qemu_iohandler_poll对接收到的事件进行处理。

    1.2.2         添加fd

    用户添加fd的函数为qemu_set_fd_handler,参数中fd为本次添加的fd,后面分别是对该fd的处理函数(read or write),最后opaque为处理函数的参数。

    int qemu_set_fd_handler(int fd,
    
                            IOHandler *fd_read,
    
                            IOHandler *fd_write,
    
                            void *opaque)
    
    {
    
        return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
    
    }

    可见该函数直接调用了qemu_set_fd_handler2:

    int qemu_set_fd_handler2(int fd,
    
                             IOCanReadHandler *fd_read_poll,
    
                             IOHandler *fd_read,
    
                             IOHandler *fd_write,
    
                             void *opaque)
    
    {
    
        IOHandlerRecord *ioh;
    
        assert(fd >= 0);
    
           //if read and write are  null,delete
    
        if (!fd_read && !fd_write) {
    
            QLIST_FOREACH(ioh, &io_handlers, next) {
    
                if (ioh->fd == fd) {
    
                    ioh->deleted = 1;
    
                    break;
    
                }
    
            }
    
        } else {//find and goto find
    
            QLIST_FOREACH(ioh, &io_handlers, next) {
    
                if (ioh->fd == fd)
    
                    goto found;
    
            }
    
            ioh = g_malloc0(sizeof(IOHandlerRecord));
    
                    //insert ioh to  io_handlers list
    
            QLIST_INSERT_HEAD(&io_handlers, ioh, next);
    
        found:
    
            ioh->fd = fd;
    
            ioh->fd_read_poll = fd_read_poll;
    
            ioh->fd_read = fd_read;
    
            ioh->fd_write = fd_write;
    
            ioh->opaque = opaque;
    
            ioh->pollfds_idx = -1;
    
            ioh->deleted = 0;
    
            qemu_notify_event();
    
        }
    
        return 0;
    
    }

    这里判断如果read和write函数均为空的话就表示本次是要delete某个fd,就遍历所有的io_handlers,对指定的fd对应的IOHandlerRecord标志delete。

    否则还有两种情况,添加或者更新。所以首先还是要从io_handlers找一下,如果找到直接更新,否则新创建一个IOHandlerRecord,然后再添加信息。具体信息内容就比较简单。

    1.2.3         处理fd

    在main_loop_wait函数中,通过os_host_main_loop_wait对fd进行监听,当然并不是它直接监听,而是通过glib的接口。

    当os_host_main_loop_wait返回后,就表示当前有可用的事件,在main_loop_wait函数中,调用了qemu_iohandler_poll函数对fd进行处理。

    void qemu_iohandler_poll(GArray *pollfds, int ret)
    
    {
    
        if (ret > 0) {
    
            IOHandlerRecord *pioh, *ioh;
    
            QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
    
                int revents = 0;
    
                if (!ioh->deleted && ioh->pollfds_idx != -1) {
    
                    GPollFD *pfd = &g_array_index(pollfds, GPollFD,
    
                                                  ioh->pollfds_idx);
    
                    revents = pfd->revents;
    
                }
    
                if (!ioh->deleted && ioh->fd_read &&
    
                    (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) {
    
                    ioh->fd_read(ioh->opaque);
    
                }
    
                if (!ioh->deleted && ioh->fd_write &&
    
                    (revents & (G_IO_OUT | G_IO_ERR))) {
    
                    ioh->fd_write(ioh->opaque);
    
                }
    
                /* Do this last in case read/write handlers marked it for deletion */
    
                if (ioh->deleted) {
    
                    QLIST_REMOVE(ioh, next);
    
                    g_free(ioh);
    
                }
    
            }
    
        }
    
    }

    具体的处理倒也简单,逐个遍历io_handlers,对于每个GpollFD,取其revents,判断delete标志并校验状态,根据不同的状态,调用read或者write回调。最后如果是delete的GpollFD,就从链表中remove掉,释放GpollFD。

    补充:针对qemu进程中线程数目的问题,从本节可以发现qemu主线程主要负责事件循环,针对每个虚拟机的VCPU,会有一个子线程为之服务,因此qemu线程数目至少要大于等于1+VCPU数目。

    以马内利!

    参考资料:

    1、qemu 2.7源码

  • 相关阅读:
    二分图 洛谷P2055 [ZJOI2009]假期的宿舍
    并查集 洛谷P1640 [SCOI2010]连续攻击游戏
    贪心 洛谷P2870 Best Cow Line, Gold
    贪心 NOIP2013 积木大赛
    快速幂 NOIP2013 转圈游戏
    倍增LCA NOIP2013 货车运输
    树形DP 洛谷P2014 选课
    KMP UVA1328 Period
    动态规划入门 BZOJ 1270 雷涛的小猫
    KMP POJ 2752Seek the Name, Seek the Fame
  • 原文地址:https://www.cnblogs.com/ck1020/p/8782032.html
Copyright © 2011-2022 走看看