一、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 标准开发流程
-
创建对应句柄/请求对象
-
启动事件监听/任务提交
-
事件触发,执行异步回调
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底层开发、工业级高性能服务。

2433

被折叠的 条评论
为什么被折叠?



