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 ;

}

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 >= );

       //if read and write are  null,delete

    if (!fd_read && !fd_write) {

        QLIST_FOREACH(ioh, &io_handlers, next) {

            if (ioh->fd == fd) {

                ioh->deleted = ;

                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 = -;

        ioh->deleted = ;

        qemu_notify_event();

    }

    return ;

}

这里判断如果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 > ) {

        IOHandlerRecord *pioh, *ioh;

        QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {

            int revents = ;

            if (!ioh->deleted && ioh->pollfds_idx != -) {

                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源码

QEMU IO事件处理框架的更多相关文章

  1. ChannelPipeline----贯穿io事件处理的大动脉

    ChannelPipeline贯穿io事件处理的大动脉 上一篇,我们分析了NioEventLoop及其相关类的主干逻辑代码,我们知道netty采用线程封闭的方式来避免多线程之间的资源竞争,最大限度地减 ...

  2. IO流框架

    目录 IO流框架总结 字节流 字符流 IO流框架总结 普通IO / NIO 字节流 字节流是万能流,但是在处理字符方面有时候不太方便,一般用来处理二进制文件 字节输入流 InputStream int ...

  3. Java知识点总结——IO流框架

    IO框架 一.流的概念 概念:内存与存储设备之间传输数据的通道. 二.流的分类 按方向分类: 输入流:将<存储设备>中的内容读入到<内存>中 输出流:将<内存>中的 ...

  4. 从读写角度,带你了解数仓的IO基本框架

    摘要:本文从读取和写入的角度分别描述了行存和列存的IO模型,并对文件结构做了简单介绍. 本文分享自华为云社区<GaussDB(DWS)基本IO框架>,作者: Naibaoofficial. ...

  5. Hadoop Yarn事件处理框架源码分析

    由于想在项目中使用类似yarn的事件处理机制,就看了实现.主要是由Dispatcher.java,EventHandler.java,Service.java这3个类撑起来的. 在事件处理之前,先注册 ...

  6. 【Spring Boot】集成Netty Socket.IO通讯框架

    服务端 @Configuration public class NettySocketConfig { private static final Logger logger = LoggerFacto ...

  7. Spring Boot集成Reactor事件处理框架的简单示例

    1. Reactor简介 Reactor 是 Spring 社区发布的基于事件驱动的异步框架,不仅解耦了程序之间的强调用关系,而且有效提升了系统的多线程并发处理能力. 2. Spring Boot集成 ...

  8. FD.io vpp 框架转发图

    在ip4-icmp-input 与 ip4-udp-input后可以注册后续的处理函数,ip4-icmp-input根据 icmp的报文类型选择相应的处理函数,而ip4-udp-input根据端口选择 ...

  9. IO流框架关系总结(关系图)

    字节流和字符流关系图  打印流和序列化流关系图

随机推荐

  1. sutdio中替换全局方法

    Ctril Shift R Text to find :是要搜索的内容或者要被替换的内容 Replace with :是要替换的内容 Preview:是可以看到预览 在Scope的选项卡里 Whole ...

  2. Flask-Script应用实例

    #coding=utf-8 #Flask-Script是一个可以在flask应用外部编写脚本的扩展 #常用功能: #运行一个开发的服务器 #python shell中操作数据库看 #命令行任务 #fr ...

  3. N76E003的定时器/计数器 0和1

    定时器/计数器 0和1N76E003系列定时器/计数器 0和1是2个16位定时器/计数器.每个都是由两个8位的寄存器组成的16位计数寄存器. 对于定时器/计数器0,高8位寄存器是TH0. 低8位寄存器 ...

  4. STL——关联式容器

    一.关联式容器 标准的STL关联式容器分为set(集合)/map(映射表)两大类,以及这两大类的衍生体multiset(多键集合)和 multimap(多键映射表).这些容器的底层机制均以RB-tre ...

  5. Could not find the main class: org.apache.catalina.startup.Bootstrap. Program will exit.

    出现此异常原因是jdk环境变量未配置正确

  6. 异构GoldenGate 12c 单向复制配置

    1.分别在windows2008.linux平台部署oracle 11.2.0.4 2.分别在windows2008.linux平台部署gg. 2.1 windows平台: gg的安装目录位 C:\o ...

  7. 从零开始学习Hadoop--第4章 序列化(转载)

    作者对序列化的描述浅显易懂!(https://www.douban.com/note/313096752/) 1. 序列化从头说 在面向对象程序设计中,类是个很重要的概念.所谓“类”,可以将它想像成建 ...

  8. Laravel 5.1 中如何使用模型观察者

    有时候我们需要在一个表更改后,触发某个事件,最常见的比如,首页推荐商品 1 更改了,需要清空所有首页商品缓存. 首先我们需要在建立一个观察者类,比如 App\Model\Observers\Proje ...

  9. cJson 创建 读取

    关于c语言操作json,cjson还挺好用,许多操作已经帮开发员封装好了,使用起来很方便.资源下载地址为:http://sourceforge.net/projects/cjson/在test.c文件 ...

  10. 前端代码在线调试&分享网站

    1.RunJs 2.CodePen 3.JsFiddle