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. hdu 5150(水题)

    Sum Sum Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  2. 代码编辑器[0] -> Vim/gVim[0] -> 基于 Python 的 gVim 环境配置(Windows)

     环境配置 / Environment Setup 基于Python开发的 gVim 环境配置(Windows) 使用方式参考 Vim 的使用. 1 基于vundle进行配置 Vim有多个扩展管理器, ...

  3. 线段树【p2801】教主的魔法

    Description 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.--.N. 每个人的 ...

  4. POJ 2135 Farm Tour (费用流)

    [题目链接] http://poj.org/problem?id=2135 [题目大意] 有一张无向图,求从1到n然后又回来的最短路 同一条路只能走一次 [题解] 题目等价于求从1到n的两条路,使得两 ...

  5. 六. 异常处理4.try和catch的使用

    尽管由Java运行时系统提供的默认异常处理程序对于调试是很有用的,但通常你希望自己处理异常.这样做有两个好处.第一,它允许你修正错误.第二,它防止程序自动终止.大多数用户对于在程序终止运行和在无论何时 ...

  6. linux命令详解:df命令

    转:http://www.cnblogs.com/lwgdream/p/3413579.html 前言 df命令用来查看系统的space和inode使用情况,也是常用命令之一 使用说明 -a 显示所有 ...

  7. cocurrent包ExecutorService线程池

    16. 执行器服务 ExecutorService java.util.concurrent.ExecutorService 接口表示一个异步执行机制,使我们能够在后台执行任务.因此一个 Execut ...

  8. IdHTTPServer(indy10)开发REST中间件

    IdHTTPServer(indy10)开发REST中间件 浏览器通过“get”方式查询数据URL样例:http://127.0.0.1:7777/query?sql=select * from t1 ...

  9. 开源 ≠ 免费,开源协议License详解

    凡是做过软件开发的,都会接触到开源软件或开源组件,它们都会基于某种协议来提供源码和授权,那么这些开源协议到底有哪些约束呢? 在介绍之前,必须告诉大家,针对开源协议,必须打消“开源 = 免费”这个念头, ...

  10. 图文介绍openLDAP在windows上的安装配置

    目录 概述 测试环境 安装过程 配置启动 客户端介绍 多级DC的ldif文件的配置 [一].概述 什么叫LDAP呢,概念的东西这里就不多讲了,网上搜索下有很多,本文的重点是介绍如何在windows平台 ...