#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org
GLIB 的事件机制比较复杂,设计好多概念和模块,网络上相关的介绍也比较乱, 最好的理解方式就是自己动手敲一遍相关代码,然后做一个总结。
GLib 事件机制介绍
GLib 的事件框架主要有以下组件:
- GMainLoop : 管理所有可用事件源, 添加初始事件源后,会不断检查来自每个事件源的新事件并分派相关函数处理.
- GMainContext : 每个时间源创建之后都需要关联到一个 GMainContext 中。
- GSource : 具体的事件源对象.
GLib 的事件分发机制流程:
- 创建一个 GMainContext
- 创建一个 GSource, 指定该 source 的相关元素,如准备事件,检查事件, 分发执行函数等。将需要监控的事件源,回调函数等(如果有,如文件描述符) 添加到source中,并将这个source关联到 GMainContext上。
- 创建一个 GMainLoop,并将 GMainContext 关联到这个loop上。
- 运行 loop。
此后 GMainLoop 将会按照我们设定的条件周而复始的开始处理事件,如对于文件读写, 当有文件可读/可写时候,调用相关的回调函数读写等。
Demo
下面的 demo 展示从事件创建 -> 添加到context -> 关联到loop,运行的基本流程
#include <glib.h>
#include <stdio.h>
#include <string.h>
typedef struct DemoSource {
GSource source;
char *data;
} DemoSource;
/**
* 如果 GSourceFuncs::prepare() 返回 TRUE, 则 GSourceFuncs::dispatch 立即执行
* 如果 GSourceFuncs::prepare() 返回 FALSE:
* --> 等待 p_timeout 时间之后:
* --> 如果 GSourceFuncs::check() 返回 TRUE, 则 GSourceFuncs::dispatch 立即执行
* --> 如果 GSourceFuncs::check() 返回 FALSE, 继续进入 GSourceFuncs::prepare()
*/
static gboolean source_prepare(GSource *source, gint *p_timeout)
{
static int i = 0;
if (i++ % 4 == 0) {
printf("%s:%d\n", __FUNCTION__, __LINE__);
return TRUE;
}
printf("%s:%d\n", __FUNCTION__, __LINE__);
*p_timeout = 1000;
return FALSE;
return FALSE;
}
static gboolean source_check(GSource *source)
{
printf("%s:%d\n", __FUNCTION__, __LINE__);
return TRUE;
}
static gboolean source_dispatch(GSource *source, GSourceFunc callback,
gpointer user_data)
{
printf("%s:%d\n", __FUNCTION__, __LINE__);
return TRUE;
}
static GSourceFuncs source_funcs = {
.prepare = source_prepare,
.check = source_check,
.dispatch = source_dispatch,
};
int main(int argc, char *argv[])
{
char *data = "demo source";
GMainContext *main_context;
GSource *source = g_source_new(&source_funcs, sizeof(DemoSource));
DemoSource *demo_source = (DemoSource *)source;
demo_source->data = data;
main_context = g_main_context_new();
g_source_attach(source, main_context);
g_source_unref(source);
GMainLoop *loop = g_main_loop_new(main_context, FALSE);
g_main_loop_run(loop);
g_main_loop_unref(loop);
return 0;
}
对于文件事件源,类似的处理,只不过多了文件是否可读写的相关操作。
#include <glib.h>
#include <stdio.h>
#include <string.h>
typedef struct DemoSource {
GSource source;
GIOChannel *channel;
GPollFD fd;
} DemoSource;
/**
* 对文件类型的source,GSourceFuncs::prepare() 返回 -1,
* 然后在 GSourceFuncs::check() 中检查是否读写事件已经就绪.
* 如果 check 检查到就绪,dispatch 调用callback执行。
*/
static gboolean source_prepare(GSource *source, gint *p_timeout)
{
printf("%s:%d\n", __FUNCTION__, __LINE__);
*p_timeout = -1;
return FALSE;
}
static gboolean source_check(GSource *source)
{
DemoSource *demo_source = (DemoSource *)source;
if (demo_source->fd.revents != demo_source->fd.events) {
return FALSE;
}
return TRUE;
}
static gboolean source_dispatch(GSource *source, GSourceFunc callback,
gpointer user_data)
{
if (callback) {
callback(source);
}
return TRUE;
}
static GSourceFuncs source_funcs = {
.prepare = source_prepare,
.check = source_check,
.dispatch = source_dispatch,
};
gboolean watch(gpointer user_data)
{
char *buf = NULL;
gsize len = 0;
DemoSource *source = (DemoSource *)user_data;
g_io_channel_read_line(source->channel, &buf, &len, NULL, NULL);
if (len > 0) {
printf("%s", buf);
}
g_free(buf);
return TRUE;
}
int main(int argc, char *argv[])
{
GMainContext *main_context;
GSource *source = g_source_new(&source_funcs, sizeof(DemoSource));
DemoSource *demo_source = (DemoSource *)source;
demo_source->channel = g_io_channel_new_file("/tmp/test.txt", "r", NULL);
demo_source->fd.fd = g_io_channel_unix_get_fd(demo_source->channel);
demo_source->fd.events = G_IO_IN;
g_source_add_poll(source, &demo_source->fd);
g_source_set_callback(source, watch, NULL, NULL);
main_context = g_main_context_new();
g_source_attach(source, main_context);
g_source_unref(source);
GMainLoop *loop = g_main_loop_new(main_context, FALSE);
g_main_loop_run(loop);
g_main_loop_unref(loop);
return 0;
}
一般使用中直接使用 g_io_create_watch/g_io_add_watch
就可以简化相关代码