Qemu基于Glib事件框架浅析

# qemu事件框架

qemu事件框架基于Glib事件循环
简述Glib事件循环:
Glib提供了一套事件分发接口,在这套接口上注册事件源以及对应的回调函数,在条件满足时触发回调。Glib的核心为poll机制,通过poll查看事件源是否满足触发条件,满足则调用相应函数。
Glib管理的事件源可以在一个线程中处理,也可不同线程,取决于事件源所在上下文,一个线程可有多个上下文,一个上下文只能在一个线程
qemu基于Glib但并不是全套照搬,其中将循环入口函数改为while进行循环。

[Glib事件循环状态机]

在这里插入图片描述

 

如上图所示,Glib事件循环分为上图四个部分:初始化、准备、poll、调度。
使用者需要在这四个接口处设置对应的处理函数

prepare: gboolean (*prepare) (GSource *source, gint *timeout_);
Glib初始化完成后会调用此接口,此接口返回TRUE表示事件源都已准备好,告诉Glib跳过poll直接检查判断是否执行对应回调。返回FALSE表示需要poll机制监听事件源是否准备好,如果有事件源没准备好,通过参数timeout指定poll最长的阻塞时间,超时后直接返回,超时接口可以防止一个fd或者其它事件源阻塞整个应用


query:gint (g_main_context_query) (GMainContext *context, gint max_priority, gint *timeout_, GPollFD *fds, gint n_fds);
Glib在prepare完成之后,可以通过query查询一个上下文将要poll的所有事件,这个接口需要用户主动调用


check:gboolean (*check) (GSource *source);
Glib在poll返回后会调用此接口,用户通过注册此接口判断哪些事件源需要被处理,此接口返回TRUE表示对应事件源的回调函数需要被执行,返回FALSE表示不需要被执行


dispatch:gboolean (*dispatch) (GSource *source, GSourceFunc callback, gpointer user_data);
Glib根据check的结果调用此接口,参数callback和user_data是用户通过g_source_set_callback注册的事件源回调和对应的参数,用户可以在dispatch中选择直接执行callback,也可以不执行

qemu事件循环初始化
在qemu_init_main_loop中进行
```
static void
qemu_init_main_loop()
{
    GSource *src;

    qemu_aio_context = aio_context_new();        // 创建qemu定制的事件源qemu_aio_context
    gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));                                                                                                                                                                                   

    src = aio_get_g_source(qemu_aio_context);    // 从定制事件源中获取Glib原始的事件源
    g_source_set_name(src, "aio-context");        // 设置事件源名称
    g_source_attach(src, NULL);    // 将事件源添加到Glib默认事件循环上下文
    g_source_unref(src);

    src = iohandler_get_g_source();    // 获取另一个定制的事件源 iohandler_ctx
    g_source_set_name(src, "io-handler");
    g_source_attach(src, NULL);    // 将事件源添加到Glib默认事件循环上下文
    g_source_unref(src);      
}
```


qemu中事件源应以Glib提供事件源GSource作为第一成员:

```
struct AioContext { 
    GSource source;    //描述符监听
    struct QEMUBH *first_bh; //用于实现下半部
    EventNotifier notifier; //用于事件通知
    QEMUTimerListGroup tlg; //用于时钟事件源监听
}
```

事件源初始化函数:

```
AioContext *aio_context_new(Error **errp)
{
    int ret;
    AioContext *ctx;

    ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));    // 事件源的创建,传入的size是AioContext的大小
    aio_context_setup(ctx);
    /*初始化事件源的其它部分*/
    ret = event_notifier_init(&ctx->notifier, false);    // 初始化实现事件通知的成员notifier
    if (ret < 0) {
        error_setg_errno(errp, -ret, "Failed to initialize event notifier");
        goto fail;
    }
    ......
}
```

替代Glib中g_main_loop_run事件循环入口函数
```
    while (!main_loop_should_exit()) {
        main_loop_wait(false);
    }

```

选择poll方式

```
/*
如果qemu配置了PPOLL,使用PPOLL实现poll探测,否则使用g_poll实现poll探测
*/
int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout)
{
#ifdef CONFIG_PPOLL
    if (timeout < 0) {    // 如果timeout小于0,如果IO没有准备好,永远阻塞
        return ppoll((struct pollfd *)fds, nfds, NULL, NULL);    
    } else {            // 否则,阻塞一段时间后返回
        struct timespec ts;
        int64_t tvsec = timeout / 1000000000LL;
        /* Avoid possibly overflowing and specifying a negative number of
         * seconds, which would turn a very long timeout into a busy-wait.
         */
        if (tvsec > (int64_t)INT32_MAX) {
            tvsec = INT32_MAX;
        }
        ts.tv_sec = tvsec;
        ts.tv_nsec = timeout % 1000000000LL;
        return ppoll((struct pollfd *)fds, nfds, &ts, NULL);
    }
#else   
    return g_poll(fds, nfds, qemu_timeout_ns_to_ms(timeout));
#endif  

```

qemu自行定义了aio_handlers链表,用于存放AioContext中事件源的处理函数
其第一个成员pfd用于poll查询、监听
[AioHandler结构]

在这里插入图片描述

 

自定义事件源 AioContext中的first_bh为下半部链表
下半部及BH,用于延迟实现功能
将优先级较低的事件的实现进行延迟,使得其他事件有更多的计算机资源。
BH事件触发方式主要为两种:
①执行线程poll到fd准备好之后进行回调执行
②通过eventfd api通知调度
qemu在aio_context_new创建AioContext的时候会通过event_notifier_init初始化EventNotifier,将EventNotifier中的rfd设置aio_set_event_notifier为事件循环要监听的fd。当一个线

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值