linux C -glib库的基本使用
一、课程介绍
GLib 和 libuv。这两个库分别代表了传统 GNOME 生态与现代异步网络编程的两种风格,是从底层走向工程实践的重要桥梁。
二、库概览与跨平台原理
2.1 GLib 简介
GLib 是 GNOME 项目的底层公共库,提供标准化的 API,包括内存管理、数据结构、线程、日志等内容,封装了大量原始系统调用,使开发更安全、跨平台。
适合人群: 需要在 Linux/Windows 上编写后台逻辑、跨平台桌面程序等。
常见用途:
GTK 应用开发
跨平台 CLI 工具
线程安全的数据结构封装
GLib 特点(重难点):
✅ 跨平台性强:抽象系统调用,支持 Windows/Linux
✅ 面向对象思维:虽为 C 语言实现,但具备类、信号、对象等机制(通过 GObject)
✅ 丰富的数据结构库:如链表、哈希表、队列,全部线程安全版本
✅ 线程模型完整:GThread、GMutex、GCond 等,适配 C11 pthread
✅ 灵活的事件循环:
GMainLoop
和GSource
适合写任务调度器✅ 日志和测试内建:
g_log
和g_test
提高可维护性
使用 GLib 可能遇到的难点:
函数名长、类型多,初期记忆成本高
不熟悉宏、类型系统(如
gpointer
、gchar
)时调试困难手动内存释放仍然需要注意(如
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_WIN32
、G_OS_UNIX
和统一封装函数,如g_thread_create()
对应 pthread 或 Windows threadlibuv:用 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 <glib.h>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 <glib.h>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.h
和 glib/gtestutils.h
:
#include <glib.h>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 的事件循环(
GMainLoop
和GSource
),以及集成到网络事件驱动中。
3.7 GLib 事件循环概念(图示)
GLib 的事件循环是实现异步任务的核心机制。
GMainContext 管理所有事件源,负责监听和分发事件
GMainLoop 是事件循环本体,调用
g_main_context_iteration()
驱动事件执行GSource 代表一个具体事件源,绑定回调函数执行具体任务
事件循环使用示例
#include <glib.h>
#include <stdio.h>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 <uv.h>
#include <stdio.h>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_t
、uv_connect_t
和回调机制完成跨平台 TCP 通信。
基本流程:
初始化
uv_loop_t
和uv_tcp_t
发起连接或监听
通过回调处理连接、数据读写事件
4.3 线程池与异步任务
libuv 内置线程池用于执行阻塞任务,避免主事件循环阻塞。
#include <uv.h>
#include <stdio.h>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;
}