Qmeu 采用了基于事件驱动的架构,所有的事件都在一个事件循环(event loop)中被处理,系统中默认的事件循环是在main-loop.c 中的主循环(main loop)。我们也可以使用 –object iothread,id=my-iothread自己创建事件循环。

Qemu 中的事件架构来源于glib,其实qemu本身就是基于glib的,qemu中有大量的概念来源于glib,所以在学习qemu之前先了解一下glib有助于更快的理解qemu。下面首先介绍一下glib中的事件机制。

Glib 中的事件处理

Glib中是由一个主事件循环(main event loop)来负责处理所有的事件源(source),事件源包括文件描述符(纯文件、管道或者socket)和超时。新的事件源可以通过 g_source_attach()来添加。为了实现在不同的线程中处理多个、独立的事件源,每一个事件源都关联一个主上下文GMainContext的数据结构。一个GMainContex只能在一个线程中运行,但不同线程中的事件源可以互相添加或删除。GMainContext中的事件源会在GMainContext关联的主事件循环中进行检查和发送(dispatch)。

新的事件源类型可以通过包含GSource结构体来创建。新的事件源类型中GSource结构体必须是第一个成员,其他成员放在其后。要创建一个新事件源类型实例,可以调用g_source_new()函数,传入新的事件源类型大小和一个GSourceFuncs类型的变量,这个变量决定了新的事件源类型的控制方式。

新的事件源通过两种方式跟主上下文交互。第一种方式是GSourceFuncs中的prepare函数可以设置一个超时时间,来决定主事件循环中轮询的超时时间;第二种方式是通过g_source_add_poll()函数来添加文件描述符。

主上下文的一次循环包含四个步骤,分别由四个函数实现:g_main_context_prepare(), g_main_context_query(), g_main_context_check() 和 g_main_context_dispatch(),其状态转换图如下:

下面分别简单介绍一下这四个函数的作用:

  1. g_main_context_prepare():对于没有设置G_SOURCE_READY标志的source,调用source->source_funcs->prepare函数,如果返回TRUE,则设置source的G_SOURCE_READY;调用prepare函数时会通过参数返回一个超时时间,选取最小的一个超时时间赋值给context->timeout。
  2. g_main_context_query():从context->poll_records中返回指定个数的fd,返回 context->timeout,context->poll_changed设为FALSE.
  3. g_main_context_check():如果context->poll_changed 为TRUE,则返回FALSE;否则复制传入的fds的revents到相应的 context->poll_records->fd->revents中;遍历所有的source,对未设置 G_SOURCE_READY 标志的source调用其 check 函数;将已经设置 G_SOURCE_READY 标志的source 添加到 context->pending_dispatches中;
  4. g_main_context_dispatch():清除 context->pending_dispatches 中 source 的 G_SOURCE_READY 标志,然后调用其 dispatch 函数;

上面就是整个事件的处理流程,我们需要做的就是把新的source加入到这个处理流程中,glib会负责处理source上注册的各种事件源。Glib中有两个添加函数,分别实现将source 加入到GMaincontext和将fd加入到source的功能:

  1. g_source_attach(): 将 source->poll_fds中的文件描述符加入到 context->poll_records中;source添加到 context 的source 链表中;
  2. g_source_add_poll(): 将 fd 加入到 source->poll_fds中,然后再加入到 context->poll_records中,设置 context->poll_changed 为 TRUE.

Qemu 中的事件处理

下面介绍一下qemu 是如何使用这一套事件处理流程的。Qemu是基于glib 开发的,继承了很多glib的概念,struct AioContext 就是按照 glib 的source 创建原则新建的一个事件源类型,用来处理信号,中断等事件,其内容如下:

struct AioContext {

GSource source;

RFifoLock lock;

QLIST_HEAD(, AioHandler) aio_handlers;

int walking_handlers;

uint32_t notify_me;

QemuMutex bh_lock;

struct QEMUBH *first_bh;

int walking_bh;

bool notified;

EventNotifier notifier;

QEMUBH *notify_dummy_bh;

struct ThreadPool *thread_pool;

QEMUTimerListGroup tlg;

int external_disable_cnt;

int epollfd;

bool epoll_enabled;

bool epoll_available;

};

AioContext 拓展了glib 中source的功能,不但支持fd、超时的轮询,还模拟内核中的下半部机制实现了事件的异步通知功能,其中的通知功能是基于 eventfd 实现的。

AioContext 本质上还是一个 source,我们在上文中提到,source有一个很重要的成员 GSourceFuncs,它控制着source在主上下文中的控制方式。AioContext 的 GSourceFuncs 定义如下:

static GSourceFuncs aio_source_funcs = {

aio_ctx_prepare,

aio_ctx_check,

aio_ctx_dispatch,

aio_ctx_finalize

};

这几个函数分别在g_main_context_prepare(), g_main_context_check() 和 g_main_context_dispatch() 中被调用。下面分别介绍一下这几个函数的主要功能:

  1. aio_ctx_prepare 会调用 aio_compute_timeout 来计算需要的超时时间,这个超时时间是在轮询过程中使用的,它是由 AioContext 中注册的bh的属性决定的,当AioContext 中注册的所有的bh 都是空闲的时,则返回一个有效的超时时间;当至少有一个bh不是空闲的时,则返回0,从而保证bh会被尽快执行。

struct QEMUBH {

AioContext *ctx;

QEMUBHFunc *cb;

void *opaque;

QEMUBH *next;

bool scheduled;

bool idle;

bool deleted;

};

2. aio_ctx_check 用来检查如果bh、fd或timer存在就绪,则返回TRUE,从而调用 g_main_context_dispatch()

3. aio_ctx_dispatch 调用aio_dispatch,依次执行就绪的bh、fd和timer,完成依次主循环。

qemu会在初始化的过程中通过g_source_new 函数把 aio_source_funcs 注册到AioContext。

Qemu中常用的 AioContext 实例有四个, qemu_aio_context, iohandler->ctx,iothread 中的AioContext,描述磁盘镜像的BlockDriverState 中的 AioContext。他们负责处理的事件分别是:

  • Qemu_aio_context: VNC,QMP 命令
  • Iohandler->ctx:负责监控信号,中断,事件通知,socket等;
  • Iothread->ctx:主要负责io方面的监控;
  • Bs->ctx:负责blockjob等相关任务的监控

Qemu在初始化的过程中用 g_source_attach 函数把 qemu_aio_context和iohandler->ctx 添加到主循环。

新建qemu事件处理循环

上面是qemu效仿glib 实现的主循环,但主循环存在一些缺陷,比如在主机使用多CPU的情况下伸缩性受到限制,同时主循环使用了qemu全局互斥锁,从而导致vCPU线程和主循环存在锁竞争,导致性能下降。为了解决这个问题,qemu引入了iothread 事件循环,把一些IO操作分配给iothread,从而提高IO性能。

Iothread的创建方式是在qemu启动的时候传入–object iothread,id=my-iothread参数。在iothread线程中循环执行aio_poll,这个函数简化了glib的事件循环,只要存在就绪的fd就执行aio_dispatch,从而执行就绪的bh、fd和timer。

参考:

  1. Qemu/docs/multiple-iothreads.txt
  2. https://developer.gnome.org/glib/2.46/glib-The-Main-Event-Loop.html

Qemu事件处理机制简介的更多相关文章

  1. 【3】【MOOC】Python游戏开发入门-北京理工大学【第三部分-游戏开发之机制(事件处理机制)】

    学习地址链接:http://www.icourse163.org/course/0809BIT021E-1001873001?utm_campaign=share&utm_medium=and ...

  2. Qt事件处理机制

    研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 先给出原文 ...

  3. Qt 事件处理机制

    Qt 事件处理机制 因为这篇文章写得特别好,将Qt的事件处理机制能够阐述的清晰有条理,并且便于学习.于是就装载过来了(本文做了排版,并删减了一些冗余的东西,希望原主勿怪),以供学习之用. 简介 在Qt ...

  4. QT开发(十二)——QT事件处理机制

    一.QT事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下和松开 ...

  5. Qt之事件处理机制

    思维导读 一.事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下 ...

  6. Android休眠唤醒机制简介(二)

    本文转载自:http://blog.csdn.net/zhaoxiaoqiang10_/article/details/24408911 Android休眠唤醒机制简介(二)************* ...

  7. Cocos2d-x之事件处理机制

    |   版权声明:本文为博主原创文章,未经博主允许不得转载. 事件处理机制分为单点触屏,多点触屏,加速度事件,键盘事件和鼠标事件.在现在的智能手机中,触屏的应用比较的广泛,尤其是多点触屏事件的技术,使 ...

  8. java 事件处理机制:按下上下左右键控制小球的运动

    /** * 加深对事件处理机制的理解 * 通过上下左右键来控制一个小球的位置 */package com.test3;import java.awt.*;import javax.swing.*;im ...

  9. Android事件处理机制

    包括监听和回调两种机制. 1. 基于监听的事件处理: 事件监听包含三类对象,事件源,事件,事件监听器.Android的事件处理机制是一种委派式(Delegation)事件处理方式:普通组件(事件源)将 ...

随机推荐

  1. js中立即执行函数写法理解

    在理解了一些函数基本概念后,回头看看( function(){…} )()和( function (){…} () )这两种立即执行函数的写法,最初我以为是一个括号包裹匿名函数, 并后面加个括号立即调 ...

  2. 恢复安装过树莓派相关操作系统的TF卡容量

    原文地址:传送门 前言玩树莓派的都知道,当我们向TF卡写入系统后,在Windows下能识别的只有几百M的容量了,这主要是由于在装Linux系统的时候给TF卡分了Windows无法识别的分区,下面我用图 ...

  3. 大牛教你如何循序渐进,有效的学习JavaScript?

    首先要说明的是,咱现在不是高手,最多还是一个半桶水,算是入了JS的门.谈不上经验,都是一些教训. 这个时候有人要说,“靠,你丫半桶水,凭啥教我们”.您先别急着骂,先听我说! 你叫一个大学生去教小学数学 ...

  4. (8)oracle 表的增删改

    表的命名 表需要字母开头 只能用如下字符 A-Z,a-z,0-9,$,#. 不能使用oracle保留字 长度不能超过30 创建一张表 create table 表名(字段名 数据类型,字段名 数据类型 ...

  5. 链式前向星BFS

    本文链接:http://i.cnblogs.com/EditPosts.aspx?postid=5399068 采用链式前向星的BFS: #include <iostream> #incl ...

  6. [BZOJ 1567] Blue Mary的战役地图

    Link: BZOJ 1567 传送门 Solution: 矩阵Hash/二维$Hash$模板题 涉及到需要快速查询.匹配的题目,考虑直接上$Hash$ 矩阵$Hash$其实就是每行先各$Hash$一 ...

  7. [CF232E]Quick Tortoise

    题目大意: 给你一个$n\times m(n,m\leq 500)$的格子,有一些是障碍物.从一个格子出发只能向下或向右走,有$q$组询问,每次询问从一个点是否能够到达另一个点. 思路: 分治. 两点 ...

  8. VMware给虚拟机绑定物理网卡

    前言: 桥接模式:就是使用真实的IP地址 NAT模式:使用以VMnet 8所指定的子网中分配的IP地址,在外网信息交互中不存在这样的IP. 仅主机模式:仅用于虚拟机与真机之间的信息交互. 操作步骤: ...

  9. 待签名参数按照字段名的ascii码从小到大排序

    public static String getSign(Map<String, String> params) { Map<String, String> sortMap = ...

  10. UIViewController的生命周期及iOS程序运行顺序

    当一个视图控制器被创建,并在屏幕上显示的时候. 代码的运行顺序 1. alloc                                   创建对象,分配空间 2.init (initWit ...