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. poj 1375(解析几何)

    Intervals Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 4292   Accepted: 1288 Descrip ...

  2. php7简短而安全的数组遍历方法

    在写 PHP 的数组遍历的时候,我们通常会这样写: foreach ($definition['keys'] as $id => $val) { // ... } 但是其实这样会引起一个重要的问 ...

  3. 如何在 GitHub 建立个人主页和项目演示页面

    Git.GitHub.TortoiseGit ?http://www.cnblogs.com/guyoung/archive/2012/02/18/8030-.html GitHub Github官网 ...

  4. ./configure时候遇到的问题 Cannot find install-sh, install.sh, or shtool in ac-aux

    https://blog.csdn.net/anloan/article/details/17268997

  5. 原来是adblock惹的祸

    一个在本地开发好的网站,放到服务器就不行了.花了好几个小时的时间,最后试着把adblock关了,然后正常了.

  6. 51nod 1596 搬货物【贪心/二进制】

    1596 搬货物 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题  收藏  取消关注 现在有n个货物,第i个货物的重量是 2wi  ...

  7. outlook preview setup

    ow To Show Subject Above/Below Sender In Mail List In Outlook? Normally in the compact view of a mai ...

  8. jsp homework(*)

    3.编写一个JSP程序实现手表的功能,显示当前时间(时:分:秒),并不停地自动刷新时间. 方法一[scriptlet] <%@ page language="java" im ...

  9. 【微信】微信小程序 微信开发工具中新创建的json文件,编译报错VM1781:2 pages/module/module.json 文件解析错误 SyntaxError: Unexpected end of JSON input

    如果新创建报错:编译报错VM1781:2 pages/module/module.json 文件解析错误  SyntaxError: Unexpected end of JSON input 解决方法 ...

  10. 【Linux】CentOS7 上使用yum安装和卸载软件【yum安装wine举例】

    关于yum的相关解释,请 man yum 自行查看. 配置常用源:http://www.cnblogs.com/sxdcgaq8080/p/7516186.html yum的使用类似于在windows ...