> 技术文档 > linux C -glib库的基本使用

linux C -glib库的基本使用


一、课程介绍

GLiblibuv。这两个库分别代表了传统 GNOME 生态与现代异步网络编程的两种风格,是从底层走向工程实践的重要桥梁。


二、库概览与跨平台原理

2.1 GLib 简介

GLib 是 GNOME 项目的底层公共库,提供标准化的 API,包括内存管理、数据结构、线程、日志等内容,封装了大量原始系统调用,使开发更安全、跨平台。

适合人群: 需要在 Linux/Windows 上编写后台逻辑、跨平台桌面程序等。

常见用途:

  • GTK 应用开发

  • 跨平台 CLI 工具

  • 线程安全的数据结构封装

GLib 特点(重难点):

  • 跨平台性强:抽象系统调用,支持 Windows/Linux

  • 面向对象思维:虽为 C 语言实现,但具备类、信号、对象等机制(通过 GObject)

  • 丰富的数据结构库:如链表、哈希表、队列,全部线程安全版本

  • 线程模型完整:GThread、GMutex、GCond 等,适配 C11 pthread

  • 灵活的事件循环GMainLoopGSource 适合写任务调度器

  • 日志和测试内建g_logg_test 提高可维护性

使用 GLib 可能遇到的难点:

  • 函数名长、类型多,初期记忆成本高

  • 不熟悉宏、类型系统(如 gpointergchar)时调试困难

  • 手动内存释放仍然需要注意(如 g_free, g_strfreev

2.2 libuv 简介

libuv 是一个跨平台的异步 IO 库,广泛用于 Node.js、Luvit 等项目中,以事件驱动的方式处理网络、文件、线程等任务,性能极高。

适合人群: 有服务端开发或并发性能需求的开发者。

常见用途:

  • 网络服务器

  • CLI 工具中的文件/事件监听

  • 跨平台异步框架的底层支撑

libuv 特性与重难点:

  • 事件驱动模型:IO 不阻塞,函数立即返回,通过回调处理完成事件(Reactor 模式)

  • 跨平台统一接口:隐藏复杂平台差异,开发体验一致(例如 IOCP vs epoll)

  • 线程池支持:适合 CPU 密集或阻塞 IO,通过 uv_queue_work 调用

  • 高度模块化:所有功能以 uv_*_t 结构为中心,便于理解生命周期

  • 性能优越:大量 Node.js 实践验证

学习难点:

  • 需要理解事件驱动编程范式,与传统同步方式不同

  • 回调嵌套逻辑较深,调试复杂

  • 每个模块初始化结构体略显繁琐(需严格遵循 init/start/run

2.3 跨平台机制

  • GLib:使用宏如 G_OS_WIN32G_OS_UNIX 和统一封装函数,如 g_thread_create() 对应 pthread 或 Windows thread

  • libuv:用 CMake + 条件编译封装 epoll/kqueue/IOCP/IOURING 等底层系统调用


三、GLib 使用详解

GLib 的设计思路是将复杂系统调用抽象为统一接口,大大提升可移植性。

3.1 GLib 模块结构图

 +--------------------+ | glib.h | +--------------------+ | +----+----+--------------------+ | |  | +------+ +--------+ +-----------------+ |GList | |GThread| ... |GMainLoop/GSource| +------+ +--------+ +-----------------+ ↑  ↑  ↑ 数据结构 线程控制 事件/异步处理

3.2 内存管理与基本类型

GLib 定义了很多基础类型:

  • gint/guint:有符号/无符号 int

  • gchar:char

  • gboolean:布尔类型(TRUE/FALSE

内存申请与释放:

#include int main() { gchar *name = g_strdup(\"Hello\"); // 复制字符串(分配内存) g_print(\"%s\\n\", name); g_free(name); // 释放内存 return 0;}

3.3 字符串操作

GLib 提供了 GString 类型,支持动态字符串(类似 C++ std::string):

GString *str = g_string_new(\"Hello\");g_string_append(str, \" World\");g_print(\"%s\\n\", str->str);g_string_free(str, TRUE);

也可以使用常用函数:

  • g_ascii_strcasecmp() 比较大小写无关

  • g_strsplit() 拆分字符串

  • g_strjoinv() 拼接字符串数组


3.4 数据结构

GList / GSList

#include GSList *list = NULL;list = g_slist_append(list, \"apple\");list = g_slist_append(list, \"banana\");for (GSList *l = list; l != NULL; l = l->next) { g_print(\"%s\\n\", (gchar*)l->data);}g_slist_free(list);

GHashTable

GHashTable *map = g_hash_table_new(g_str_hash, g_str_equal);g_hash_table_insert(map, \"name\", \"Alice\");g_print(\"name = %s\\n\", g_hash_table_lookup(map, \"name\"));g_hash_table_destroy(map);

3.5 多线程与线程池

GLib 支持跨平台线程封装:

void* worker(gpointer data) { g_print(\"Worker thread: %s\\n\", (gchar*)data); return NULL;}g_thread_new(\"my-thread\", worker, \"hello from thread\");g_usleep(100000);

线程池:

GThreadPool *pool = g_thread_pool_new(worker, NULL, 4, FALSE, NULL);g_thread_pool_push(pool, \"task1\", NULL);g_thread_pool_free(pool, FALSE, TRUE);

3.6 日志与调试

GLogLevelFlags level = G_LOG_LEVEL_WARNING;g_log(\"MyApp\", level, \"This is a warning message.\");

你可以设置自定义日志处理器:

void my_logger(const gchar *log_domain, GLogLevelFlags level, const gchar *message, gpointer user_data) { g_print(\"[LOG] %s\\n\", message);}g_log_set_handler(\"MyApp\", G_LOG_LEVEL_MASK, my_logger, NULL);g_log(\"MyApp\", G_LOG_LEVEL_INFO, \"Started application\");

3.7 单元测试

使用 glib.hglib/gtestutils.h

#include void test_addition() { g_assert_cmpint(2 + 2, ==, 4);}int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func(\"/math/add\", test_addition); return g_test_run();}

接下来可继续图示 GLib 的事件循环(GMainLoopGSource),以及集成到网络事件驱动中。


3.7 GLib 事件循环概念(图示)

GLib 的事件循环是实现异步任务的核心机制。

  • GMainContext 管理所有事件源,负责监听和分发事件

  • GMainLoop 是事件循环本体,调用 g_main_context_iteration() 驱动事件执行

  • GSource 代表一个具体事件源,绑定回调函数执行具体任务

事件循环使用示例

#include #include gboolean timeout_callback(gpointer data){ printf(\"Timeout happened!\\n\"); GMainLoop *loop = (GMainLoop *)data; g_main_loop_quit(loop); // 退出循环 return FALSE;  // 只触发一次}int main(){ GMainLoop *loop = g_main_loop_new(NULL, FALSE); g_timeout_add_seconds(1, timeout_callback, loop); g_main_loop_run(loop); g_main_loop_unref(loop); return 0;}

四、libuv 使用详解

libuv 是一个跨平台的异步 IO 库,事件驱动模型的代表。

4.1 libuv 事件循环模型

libuv 的事件循环是它的核心设计思想,所有异步操作都必须通过 uv_run() 驱动执行:

uv_loop_t *loop = uv_default_loop(); uv_run(loop, UV_RUN_DEFAULT);

  • uv_loop_t 是事件循环结构体,内部封装了定时器、IO、信号等子模块

  • uv_run 会阻塞当前线程,直到所有异步事件处理完毕(或主动关闭)

  • 所有 handle(如 socket、timer)必须 attach 到 loop 上

定时器示例

#include #include void timer_cb(uv_timer_t *handle){ printf(\"Timer triggered!\\n\"); uv_stop(uv_default_loop());}int main(){ uv_timer_t timer; uv_timer_init(uv_default_loop(), &timer); uv_timer_start(&timer, timer_cb, 1000, 0); // 延迟1000ms执行 uv_run(uv_default_loop(), UV_RUN_DEFAULT); return 0;}

4.2 异步 TCP 通信

libuv 通过 uv_tcp_tuv_connect_t 和回调机制完成跨平台 TCP 通信。

基本流程:

  • 初始化 uv_loop_tuv_tcp_t

  • 发起连接或监听

  • 通过回调处理连接、数据读写事件

4.3 线程池与异步任务

libuv 内置线程池用于执行阻塞任务,避免主事件循环阻塞。

#include #include void work_cb(uv_work_t *req){ // 这里执行耗时任务}void after_work_cb(uv_work_t *req, int status){ printf(\"Work done!\\n\");}int main(){ uv_work_t req; uv_queue_work(uv_default_loop(), &req, work_cb, after_work_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); return 0;}