Libuv 高性能异步IO库零基础入门教程

一、Libuv 核心概述

Libuv 是一款跨平台、高性能、全异步、事件驱动的C语言IO库,最初为 Node.js 底层核心开发,如今已成为工业级标准异步编程底座。

1.2 Libuv vs Libevent 核心区别

  • 文件IO:Libevent 文件IO阻塞;Libuv 内置线程池实现100%异步文件IO

  • 应用场景:Libevent 多用于传统网络服务;Libuv 适配高并发、高IO、复杂异步场景(Node.js、Rust Tokio底层)

1.3 核心设计思想

全程无阻塞轮询,所有IO、定时、线程任务全部异步回调,最大化压榨CPU与IO性能。

二、Libuv 环境搭建(Linux 全方案)

2.1 快速安装(Ubuntu/Debian 推荐)

# 更新软件源
sudo apt update

# 安装libuv开发库
sudo apt install libuv1-dev -y

# 验证版本
pkg-config --modversion libuv

2.2 源码编译安装(最新稳定版)

# 下载源码
git clone https://github.com/libuv/libuv.git
cd libuv
sh autogen.sh

# 配置编译
./configure

# 编译安装
make && sudo make install

# 更新动态库缓存
sudo ldconfig

三、Libuv 核心架构与核心组件

3.1 核心组件详解

程序核心引擎,等价于Libevent的event_base。负责调度所有事件、遍历事件队列、执行异步回调。一个程序默认一个主循环。

常驻资源对象,生命周期长,手动启动、手动关闭。包含:TCP句柄、定时器句柄、信号句柄、管道句柄等。

3. uv_request_t 单次请求
4. 内置线程池

3.2 Libuv 标准开发流程

  1. 创建对应句柄/请求对象

  2. 启动事件监听/任务提交

  3. 事件触发,执行异步回调


4.1 事件循环API

// 获取系统默认事件循环(最常用)
uv_loop_t* uv_default_loop(void);

// 初始化自定义事件循环
int uv_loop_init(uv_loop_t* loop);

// 启动事件循环
// mode: UV_RUN_DEFAULT 一直运行直到无事件
int uv_run(uv_loop_t* loop, uv_run_mode mode);

// 关闭循环、释放资源
int uv_loop_close(uv_loop_t* loop);

4.2 资源管理核心API

// 关闭句柄,异步释放资源
void uv_close(uv_handle_t* handle, uv_close_cb close_cb);

// 判断循环是否存活
int uv_loop_alive(const uv_loop_t* loop);

五、基础实战1:最简Libuv程序(Hello EventLoop)

理解Libuv事件循环运行机制

#include <stdio.h>
#include <uv.h>

int main()
{
    // 1. 获取默认事件循环
    uv_loop_t *loop = uv_default_loop();

    printf("Libuv 事件循环启动成功!\n");

    // 2. 启动事件循环
    // 无任务时直接退出
    uv_run(loop, UV_RUN_DEFAULT);

    // 3. 关闭释放循环资源
    uv_loop_close(loop);
    return 0;
}

六、基础实战2:Libuv 高并发TCP回声服务器

对标Libevent回声服务器,实现多客户端并发连接、异步数据收发,全程非阻塞异步

6.1 完整规范源码(统一注释风格)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>

// 服务监听端口
#define PORT 8888
// 数据缓冲区大小
#define BUF_SIZE 1024

// 客户端连接结构体
typedef struct {
    uv_tcp_t client;    // TCP客户端句柄
    char buf[BUF_SIZE]; // 数据缓冲区
} ClientData;

/**
 * @brief 客户端资源释放回调
 * @param handle 客户端句柄
 */
void client_close_cb(uv_handle_t* handle)
{
    free(handle);
}

/**
 * @brief 客户端数据读取回调
 * @param client 客户端TCP句柄
 * @param nread 读取字节数
 * @param buf 缓冲区数据
 */
void read_cb(uv_stream_t* client, ssize_t nread, const uv_buf_t* buf)
{
    // nread < 0 代表连接断开/异常
    if (nread <= 0)
    {
        printf("客户端断开连接\n");
        // 关闭客户端句柄,触发释放回调
        uv_close((uv_handle_t*)client, client_close_cb);
        return;
    }

    // 打印接收数据
    printf("收到客户端数据:%s\n", buf->base);

    // 回声:原样写回客户端
    uv_buf_t wbuf = uv_buf_init(buf->base, nread);
    uv_write_t* write_req = malloc(sizeof(uv_write_t));
    uv_write(write_req, client, &wbuf, 1, NULL);
}

/**
 * @brief 新客户端连接回调
 * @param server 服务端TCP句柄
 * @param status 连接状态
 */
void connection_cb(uv_stream_t* server, int status)
{
    if (status != 0)
    {
        printf("新连接异常\n");
        return;
    }

    // 分配客户端内存
    ClientData* data = (ClientData*)malloc(sizeof(ClientData));
    // 初始化客户端TCP句柄
    uv_tcp_init(uv_default_loop(), &data->client);

    // 接受新连接
    uv_accept(server, (uv_stream_t*)&data->client);

    // 注册读事件回调,异步监听客户端数据
    uv_read_start((uv_stream_t*)&data->client, 
        // 缓冲区分配回调
        [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf){
            buf->base = malloc(suggested_size);
            buf->len = suggested_size;
        },
        read_cb);
}

int main()
{
    // 1. 初始化服务端TCP句柄
    uv_tcp_t server;
    uv_tcp_init(uv_default_loop(), &server);

    // 2. 绑定IP端口
    struct sockaddr_in addr;
    uv_ip4_addr("0.0.0.0", PORT, &addr);
    uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);

    // 3. 开启监听,注册连接回调
    uv_listen((uv_stream_t*)&server, 128, connection_cb);
    printf("Libuv TCP回声服务器启动,端口:%d\n", PORT);

    // 4. 启动事件循环
    uv_run(uv_default_loop(), UV_RUN_DEFAULT);

    return 0;
}

七、高级实战1:Libuv 定时器事件(一次性/循环)

Libuv 定时器精度更高、分层更细,支持一次性定时、固定间隔循环定时,底层是时间堆实现,比Libevent定时器更稳定。

7.1 定时器核心API

// 初始化定时器句柄
int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle);

// 启动定时器
// timeout: 首次延迟(ms) repeat: 循环间隔(ms)
int uv_timer_start(uv_timer_t* handle, uv_timer_cb cb, uint64_t timeout, uint64_t repeat);

// 停止定时器
int uv_timer_stop(uv_timer_t* handle);

7.2 完整定时器示例

#include <stdio.h>
#include <uv.h>

// 循环定时器句柄
uv_timer_t loop_timer;

/**
 * @brief 一次性定时器回调
 */
void once_timer_cb(uv_timer_t* handle)
{
    printf("【Libuv一次性定时器】3秒超时,执行单次任务\n");
    // 关闭一次性定时器
    uv_close((uv_handle_t*)handle, NULL);
}

/**
 * @brief 循环定时器回调
 */
void loop_timer_cb(uv_timer_t* handle)
{
    static int cnt = 0;
    cnt++;
    printf("【Libuv循环定时器】1秒间隔,第%d次执行\n", cnt);

    // 执行5次后停止循环定时器
    if (cnt >= 5)
    {
        uv_timer_stop(handle);
        uv_close((uv_handle_t*)handle, NULL);
        printf("循环定时器停止\n");
    }
}

int main()
{
    uv_loop_t* loop = uv_default_loop();

    // 1. 一次性定时器:3秒后执行
    uv_timer_t once_timer;
    uv_timer_init(loop, &once_timer);
    uv_timer_start(&once_timer, once_timer_cb, 3000, 0);

    // 2. 循环定时器:1秒间隔持续执行
    uv_timer_init(loop, &loop_timer);
    uv_timer_start(&loop_timer, loop_timer_cb, 1000, 1000);

    printf("Libuv定时器服务启动成功\n");
    uv_run(loop, UV_RUN_DEFAULT);

    return 0;
}

八、高级实战2:Libuv 异步线程池实战

Libevent 无内置线程池,阻塞任务会卡死事件循环;Libuv 原生线程池可将耗时、阻塞任务抛至后台线程,主线程永远只做事件调度,是工业级高性能核心保障。

8.1 线程任务完整示例

#include <stdio.h>
#include <unistd.h>
#include <uv.h>

/**
 * @brief 后台线程耗时任务(阻塞操作)
 * @param req 线程任务请求
 */
void thread_work(uv_work_t* req)
{
    // 模拟耗时阻塞任务
    printf("后台线程执行耗时任务...\n");
    sleep(3);
}

/**
 * @brief 任务完成主线程回调
 * @param req 线程任务请求
 * @param status 执行状态
 */
void after_work(uv_work_t* req, int status)
{
    printf("耗时任务执行完毕,回到主线程回调\n");
    free(req);
}

int main()
{
    uv_loop_t* loop = uv_default_loop();

    // 提交后台线程任务
    uv_work_t* req = (uv_work_t*)malloc(sizeof(uv_work_t));
    uv_queue_work(loop, req, thread_work, after_work);

    printf("主线程不阻塞,继续调度其他事件\n");

    uv_run(loop, UV_RUN_DEFAULT);
    return 0;
}

核心特性:耗时任务在子线程执行,不会阻塞主事件循环,完美解决IO阻塞问题。

九、Libuv 多线程事件循环模型

对标Libevent多线程模型,Libuv支持多Loop多线程隔离,每个线程拥有独立事件循环,互不干扰,并发性能更强

9.1 多线程架构优势

  • 线程间Loop独立,无锁竞争,调度效率更高

  • 充分利用多核CPU,单线程Loop无瓶颈

  • 故障隔离,单线程异常不影响整体服务

十、新手常见报错与避坑指南

10.1 编译报错 undefined reference

解决方案:编译命令必须加 -luv 链接库文件

10.2 程序内存泄漏

Libuv 严格手动管理资源,所有handle必须手动uv_close,所有malloc内存必须手动释放,否则必然内存泄漏

10.3 事件循环不退出/卡死

存在活跃handle句柄时,uv_run会永久阻塞,必须关闭所有常驻句柄才能退出循环

10.4 异步回调乱序问题

Libuv 纯异步模型,任务执行顺序不保证,业务需自行做时序控制

十一、Libevent 与 Libuv 终极选型总结

11.1 选 Libevent

简单TCP服务、轻量IO场景、老旧项目维护、入门事件驱动编程,代码简洁、上手更快。

11.2 选 Libuv

高并发、高IO、含阻塞任务、需要线程池、文件异步IO、跨平台复杂项目、Node.js底层开发、工业级高性能服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乱七八糟的屋子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值