linux C -glib库的基本使用

发布于:2025-07-25 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、课程介绍

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 <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.hglib/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 的事件循环(GMainLoopGSource),以及集成到网络事件驱动中。


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_tuv_connect_t 和回调机制完成跨平台 TCP 通信。

基本流程:

  • 初始化 uv_loop_tuv_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;
}

网站公告

今日签到

点亮在社区的每一天
去签到