Main Event Loop机制调研报告

Main Event Loop 可以管理所有可用的事件资源,这些资源可以是任意数量的不同的资源,例如文件描述符(普通文件、管道或者套接字)或者超时。用户可以定义自己其它类型的资源,但这需要使用g_resource_attach()进行关联。

在Main Event Loop中每一个事件资源都被赋予一个优先级。用户可以指定默认优先级,默认的优先级是G_PRIORITY_DEFAULT,它的值是0。值比0小的意味着更高的优先级。值比0大的意味着更低的优先级。高优先级的事件资源总是比低优先级的事件资源得到优先处理。
    GMainLoop 数据类型表示一个 Main Event Loop。使用函数 g_main_loop_new() 可以创建一个 GMainLoop。向其中添加初始化事件资源之后,就可以调用g_main_loop_run()函数进行轮询,在没有调用g_main_loop_quit()的情况下,程序将阻塞到该函数。它将继续检查新的从每个事件资源发出的新事件并处理它们。最后,当某个资源发出的事件中调用 g_main_loop_quit()时,将会退出Main Event Loop,同时g_main_loop_run()返回。
    前文已提到用户除了可以使用内建的事件资源类型之外,还可以创建和使用新的事件资源类型。通过一个 GSource 结构创建一个新的事件资源类型。新事件资源类型用一个结构体表示,并且该结构体必须以GSource为第一个元素,其他元素具体描述新事件资源的类型。可以调用 g_source_new()创建一个新事件资源类型的实例,需要将一张函数表(GSourceFuncs)和事件资源结构体的大小传递给g_source_new()函数。其中函数表中的函数决定了新的资源事件的行为。前面已经对GMainLoop、GMainContext和GSource做了介绍,他们之间的关系可以总结为图[],其中每个GMainLoop中只能有一个CMainContext,而每个CMainContext中可以有多个GSource,也就是用户可以同时对多个资源进行轮询。

图[] Main Event Loop资源组织关系图

在glib库中提供了很多函数对Main Event Loop进行设置,但是可以使用非常少的函数开始一个轮询工作。最关键的是如下几个函数:

1) g_main_loop_new() 新建一个Main Event Loop;

2) g_source_new() 用于新建一个被监控的事件资源

2) g_resource_attach() 将资源与Main Event Loop连接。

2) g_main_loop_run() 开始轮询;

这里的资源可以是glib的资源类型,也可以是前文提及的用户自己定义的资源类型。关于Main Event Loop的执行过程可以通过图[]进行理解。

图 主上下文状态图

前文已经提到GSourceFuncs,它是一个包含一个多函数集的结构体,其定义如下:

typedef struct {
  gboolean (*prepare)  (GSource    *source,
                   gint       *timeout_);
  gboolean (*check)    (GSource    *source);
  gboolean (*dispatch) (GSource    *source,
                   GSourceFunc callback,
                   gpointer    user_data);
  void     (*finalize) (GSource    *source); 
  GSourceFunc     closure_callback;          
  GSourceDummyMarshal closure_marshal; 
} GSourceFuncs;

上面代码中最上面的三个函数与轮询有关,图上可以看出这三个函数在轮询过程的不同阶段被调用。 prepare() 在所有的文件描述符被轮询前调用,对于超时事件资源该函数返回 TRUE 时说明已到定时时间,返回 FALSE 则说明未到。当所有文件描述符都被轮询完之后将会调用 check() 函数,对于超时事件资源该函数的返回值的意义与 prepare() 函数一致。 dispathc() 函数用于发送事件资源,当 prepare() 或者 check() 任意一个返回 TRUE 时,该函数将被调用。其主要是调用用户的回调函数,实现相应的功能。另,这三个函数的具体功能需要用户根据需要来实现。

以HA中超时事件为例,下面说明一下详细的实现过程。首先在系统中自定义了一个G_main_add_input((int priority, gboolean can_recurse, GSourceFuncs* funcs))函数,该函数其实是对g_source_new()和g_source_attach()函数的封装,其功能是新建一个资源并将新建的资源与CMainContext向关联。在新建该资源时用到了用户自定义的一个函数表,其具体实现如下:

static GSourceFuncs        polled_input_SourceFuncs = {
    polled_input_prepare,
    polled_input_check,
    polled_input_dispatch,
    NULL,
};

在函数 polled_input_prepare() 对信号阻塞和时间跳跃进行检测( (hb_signal_pending() != 0)||ClockJustJumped );在函数 polled_input_check() 中对超时轮询时间点进行检测( cmp_longclock(now, NextPoll) >= 0 );在函数 polled_input_dispatch() 进行具体的处理工作,只有满足上述两个函数中的条件时,该函数才会被调用,该函数调用了 check_for_timeouts() 函数来进行心跳超时的判断及相关处理工作。

上一篇:自动化爬取网易云视频,一个是获取网易云视频列表 一个是 获取视频真实播放地址


下一篇:织梦DedeCMS万能循环标签如何万能调用标签