YAOTU INSIGHTS

libuv 官方编程指南:从诞生背景到第一个事件循环程序

libuv 官方编程指南:从诞生背景到第一个事件循环程序
网络通信异步编程【免费下载链接】libuvCross-platform asynchronous I/O项目地址https://gitcode.com/gh_mirrors/li/libuv点击查看免费下载导读libuv 是一套跨平台Windows 与 Unix 同一套 API的高性能事件驱动异步 I/O 库Node.js 正是建立在其之上。本文以 libuv 仓库内官方指南的开篇章节docs/src/guide/about.rst 与 docs/src/guide/introduction.rst为核心梳理这份指南的来龙去脉、目标读者、libuv 的历史演进并结合仓库中的示例代码与核心源码uv_run、uv_loop_init带你从零跑通第一个事件循环程序掌握 handle、request、回调与错误处理等最基础也最核心的编程模型。读完本文你将具备阅读后续文件系统、网络、线程等进阶章节并独立编写 libuv 程序的能力。这份指南的由来一本缺文档催生的书about.rst的正文非常简短但信息量集中。它讲述了这份官方指南的起源2012 年 6 月 16 日的一个下午Nikhil Marathe 因为不想写代码而开始写这本书。当时他正在开发 node-taglib被 libuv 缺乏好文档这件事困扰——参考文档API reference是存在的但没有任何系统性的教程。这本书正是对没有综合教程这一痛点的回应它试图做到准确但也坦诚地承认书中可能有错误欢迎提交 Pull Request。Nikhil 特别感谢了 Marc Lehmann 撰写的 libev 详尽 man page它描述了两个库的大量共有语义libuv 早期的 API 正是基于 libev 设计的。这本书最初用 Sphinx 和 vim 制作。文档末尾的 note 记录了一个关键事实2017 年 libuv 项目将这份工作正式并入官方文档此后由项目方持续维护。这段历史解释了为什么仓库里会有一个完整的 docs/src/guide 目录——它不是临时笔记而是被官方吸收的正式教程体系。整个指南由 docs/src/guide.rst 通过 Sphinxtoctree组织包含八章introduction引言即本文主题的相邻章节basicslibuv 基础filesystem文件系统networking网络threads线程processes进程eventloops高级事件循环utilities实用工具about关于本书guide.rst 开头还带有一条官方警告这套内容刚并入官方文档时未经彻底审查发现错误可提交 issue 或 Pull Request——这与about.rst中Pull requests are encouraged的态度一脉相承。这本书写给谁两类读者与学习前提introduction.rst明确了目标读者这决定了你该以什么心态阅读这套指南系统程序员正在编写守护进程、网络服务/客户端等底层程序发现事件循环模式很适合自己决定使用 libuv。Node.js 模块作者想用 C/C 封装平台 API并向外暴露异步JavaScript 接口。这类读者在 Node.js 语境下使用 libuv需要额外参考 V8/Node.js 相关的资源因为这本书不涉及 Node.js 专属内容。指南同时强调假设读者已经熟悉 C 语言而且这套教程覆盖 libuv 的主要领域但不是讨论每个函数和数据结构的完整参考手册——完整细节请查阅官方 API 文档本文以仓库内 docs/src/api.rst 等章节为准。历史背景libev 与 IOCP 之上长出的 libuvintroduction.rst用一小节交代了 libuv 的起源这是理解它架构的关键背景2009 年Node.js 项目启动它是一个与浏览器解耦的 JavaScript 环境使用 Google V8 和 Marc Lehmann 的 libev把事件驱动的 I/O 模型与浏览器塑造过的编程语言结合起来。随着 Node.js 流行支持 Windows 变得重要但libev 只跑在 Unix 上。Windows 上对应 kqueue/(e)poll 的内核事件通知机制是IOCP。libuv 最初是 libev 或 IOCP 之上的抽象层对外提供基于 libev 的 API——也就是说同一套 API 在 Unix 上走 libev、在 Windows 上走 IOCP。在 node-v0.9.0 对应的 libuv 版本中libev 被移除libuv 成为独立的高质量系统编程库。如今 Node.js 之外的用户还包括 Mozilla 的 Rust 语言及各种语言绑定。仓库中的 README.md 把这套架构总结为 feature highlights可作为背景佐证由 epoll、kqueue、IOCP、event ports 支撑的完整事件循环异步 TCP/UDP 套接字异步 DNS 解析异步文件与文件系统操作文件系统事件fs eventANSI 转义序列控制的 TTY基于 Unix 域套接字或命名管道Windows的 IPC 与套接字共享子进程线程池信号处理高分辨率时钟线程与同步原语。仓库 docs/src/static/architecture.txt及对应架构图 docs/src/static/architecture.png以 ASCII 图直观呈现了这种分层上层是 Network I/OTCP、UDP、TTY、Pipe与 File/DNS/User Code中间是事件循环核心uv__io_t最底层是平台相关后端——Unix 上的 epoll、kqueue、event portsWindows 上的 IOCP以及用于把阻塞操作搬离事件循环的线程池Thread Pool。关于版本about.rst注明本书和代码基于 libuv v1.42.0。而当前仓库 include/uv/version.h 中的版本宏为UV_VERSION_MAJOR 1、UV_VERSION_MINOR 53、UV_VERSION_PATCH 1且UV_VERSION_IS_RELEASE 0、UV_VERSION_SUFFIX dev即当前处于1.53.1 的开发版本1.x 主版本保持 ABI 稳定API 可在小版本间向后兼容演进这点在 version.h 头注释中有明确说明。阅读示例代码时个别接口细节可能随版本演进有所调整但核心编程模型没有变化。构建 libuv 并运行示例代码introduction.rst提供了基于 autotools 的构建方式克隆仓库后执行sh autogen.sh ./configure make并特别说明不需要执行make install构建示例只需在docs/code/目录下执行make。仓库内的 docs/code 目录正是全书示例代码的存放位置包含helloworld、default-loop、idle-basic、uvstop、tcp-echo-server、uvcat、spawn、thread-create等二十余个示例。示例的构建体系由 docs/code/CMakeLists.txt 定义它通过add_subdirectory(../../ build)引入 libuv 本体然后用add_executable把SIMPLE_SAMPLES列表中的每个示例链接到静态库uv_a其中signal、progress、queue-cancel、queue-work、tty、tty-gravity等仅在非 Windows 平台构建uvwget则需要系统存在 CURL 库FIND_PACKAGE(CURL)才会编译。因此cd docs/code cmake -B build .. cmake --build build即可一次性产出全部示例可执行文件或按原文档方式用make构建。如果你更关注 libuv 本身的完整构建与测试可参考 README.md 的 Build InstructionsUnix 系平台支持 autotools 与 CMake 两种方式Windows 仅支持 CMake需要 Visual Studio 2015 Update 3 及以上版本或 VS 2017 的 MSBuild/VC 工具链测试驱动为uv_run_tests/uv_run_tests_a测试清单位于 test/test-list.h可按build/uv_run_tests_a TEST_NAME单独跑某个测试。另外本书所在的文档目录 docs 本身就是用 Sphinx 构建的about.rst提到的 Sphinx 正是它docs/requirements.txt 锁定 Sphinx 7.0.1 与 furo 主题等依赖docs/src/conf.py 从include/uv/version.h自动读取版本号用于文档版本展示Windows 用户可用 docs/make.bat 代替make执行html、man、epub、linkcheck等目标。快速上手第一个事件循环程序basics.rst是这本书真正的技术起点它奠定了全文反复使用的核心概念。事件驱动与事件循环libuv 强制一种异步、事件驱动的编程风格核心职责是提供事件循环和基于回调的 I/O 通知。事件驱动编程中应用程序对某些事件表达兴趣事件发生时由注册的回调响应libuv 负责从操作系统收集事件或监视其他事件源。事件循环通常永远运行下去伪代码如下while there are still events to process: e get the next event if there is a callback associated with e: call the callback典型的事件包括文件可写、socket 有数据可读、定时器超时。这个事件循环被封装在uv_run()中——它是使用 libuv 时的最终函数。为什么需要异步非阻塞因为传统read、fprintf等 I/O 函数是阻塞的真正的磁盘写入或网络读取相对 CPU 耗时极不对称函数直到任务完成才返回程序只能干等其他 I/O 全部被堵住。线程方案每个阻塞操作开一个线程可行但有额外开销libuv 走的是异步、非阻塞路线应用程序请求操作系统监视 socket把事件通知放入队列自己随时检查队列、按需取数据。它异步是因为表达兴趣和使用数据发生在不同的时间与空间点非阻塞是因为进程在这期间可以自由做其他事系统事件被当作普通 libuv 事件处理。basics.rst中的注释也提醒底层的实现机制worker 线程/轮询并非使用者需要关心的事。Hello World一个立即退出的循环#include stdio.h #include stdlib.h #include uv.h int main() { uv_loop_t *loop malloc(sizeof(uv_loop_t)); uv_loop_init(loop); printf(Now quitting.\n); uv_run(loop, UV_RUN_DEFAULT); uv_loop_close(loop); free(loop); return 0; }完整源码见 docs/code/helloworld/main.c这个程序立即退出因为它没有任何事件要处理——libuv 事件循环必须通过各类 API 函数明确告诉它去监视什么事件。basics.rst还特别强调了内存管理约定从 libuv v1.0 起用户应先为循环分配内存再调用uv_loop_init(uv_loop_t *)初始化这样可以接入自定义内存管理循环结束记得用uv_loop_close(uv_loop_t *)反初始化并释放存储。示例程序从不关闭循环因为程序在循环结束后就退出、系统会回收内存但生产级、尤其是长期运行的系统程序必须正确释放资源。从源码看uv_loop_init在 src/unix/loop.c 中会保存并恢复loop-data后清零整个结构体分配internal_fields初始化互斥锁与定时器堆heap_init、各句柄队列idle/async/check/prepare/handle_queue、pending_queue与watcher_queue调用平台相关的uv__platform_loop_init初始化 epoll/kqueue 等后端并注册一个内部wq_async句柄用于线程池任务完成后的唤醒。这解释了为什么一个空循环也能有条不紊地启动和关闭。默认循环如果你只需要单个循环直接使用 libuv 提供的默认循环uv_default_loop()即可#include stdio.h #include uv.h int main() { uv_loop_t *loop uv_default_loop(); printf(Default loop.\n); uv_run(loop, UV_RUN_DEFAULT); uv_loop_close(loop); return 0; }完整源码见 docs/code/default-loop/main.cbasics.rst特别提示Node.js 使用默认循环作为主循环如果你在写绑定binding务必意识到这一点。错误处理初始化或同步函数失败时返回负数异步函数失败时会把状态参数传给回调错误消息定义为UV_E*常量用uv_strerror(int)和uv_err_name(int)可分别拿到描述错误的const char *与错误名I/O 读回调文件、socket会收到nread参数nread 0表示出错其中UV_EOF是文件结尾错误常需要特殊处理。Handles 与 Requests两个最核心的概念libuv 的编程模型建立在两类对象上Handle句柄对 I/O 设备、定时器或进程的表达是不透明结构体命名形如uv_TYPE_t例如uv_tcp_t、uv_udp_t、uv_timer_t、uv_idle_t、uv_signal_t、uv_process_t、uv_fs_event_t。Handle 代表长期存在的对象通过配套的uv_TYPE_init(uv_loop_t *, uv_TYPE_t *)函数初始化。Request请求Handle 上的异步操作标识短命通常只跨一个回调用于在发起动作与回调之间保存上下文。例如 UDP 套接字用uv_udp_t表示而每次写入用一个uv_udp_send_t结构体写入完成后传给回调。basics.rst还给出了完整的类型清单handle 类型、request 类型以及既非 handle 也非 request 的信息结构体如uv_cpu_info_t、uv_interface_address_t等可参考 include/uv.h 中的实际声明。回调就是 libuv 在 watcher 感兴趣的事件发生时调用的函数应用逻辑通常写在回调里I/O watcher 回调收到读到的数据定时器回调在超时时被触发。用 idle 句柄观察生命周期下面这个例子用 idle 句柄演示 watcher 生命周期回调在事件循环每一轮都被调用一次计数达到10e6后uv_idle_stop停止 watcher因为没有活跃 watcher 了uv_run()随即退出#include stdio.h #include uv.h int64_t counter 0; void wait_for_a_while(uv_idle_t* handle) { counter; if (counter 10e6) uv_idle_stop(handle); } int main() { uv_idle_t idler; uv_idle_init(uv_default_loop(), idler); uv_idle_start(idler, wait_for_a_while); printf(Idling...\n); uv_run(uv_default_loop(), UV_RUN_DEFAULT); uv_loop_close(uv_default_loop()); return 0; }完整源码见 docs/code/idle-basic/main.cidle handle 的典型用途例如保持进程存活、穿插后台任务会在utilities一章展开讨论。另外回调式编程常需要传递上下文所有 handle 和 request 都带一个void* data成员可以在调用点设置、在回调里取回uv_loop_t也有同样的 data 成员。这是整个 C 库生态的常见模式。进阶uv_stop 与事件循环的精细控制eventloops.rst展示了对事件循环的驾驶技巧。libuv 给予用户相当大的循环控制权你可以同时摆弄多个循环甚至把 libuv 事件循环嵌入另一个事件循环驱动的库比如 Qt 的 UI 事件循环驱动一个 libuv 后端做密集系统级任务。uv_stop()用于停止事件循环但其语义容易误解关键点循环最早也要到下一轮迭代才会停止可能更晚本轮迭代中已就绪、待处理的事件仍会被处理所以uv_stop()不能当急停开关用调用uv_stop()后本轮循环不会因 I/O 而阻塞。其背后的机制在uv_run()的实现里一目了然。参考 src/unix/core.c 中uv_run的主循环while (r ! 0 loop-stop_flag 0) { can_sleep uv__queue_empty(loop-pending_queue) uv__queue_empty(loop-idle_handles); uv__run_pending(loop); uv__run_idle(loop); uv__run_prepare(loop); timeout 0; if ((mode UV_RUN_ONCE can_sleep) || mode UV_RUN_DEFAULT) timeout uv__backend_timeout(loop); uv__metrics_inc_loop_count(loop); uv__io_poll(loop, timeout); /* Process immediate callbacks (e.g. write_cb) a small fixed number of * times to avoid loop starvation.*/ for (r 0; r 8 !uv__queue_empty(loop-pending_queue); r) uv__run_pending(loop); uv__metrics_update_idle_time(loop); uv__run_check(loop); uv__run_closing_handles(loop); uv__update_time(loop); uv__run_timers(loop); ... }stop_flag由uv_stop()设置。所有 libuv 回调都在事件循环内部被调用因此在回调里调用uv_stop()也会让本轮迭代完整走完循环先更新定时器运行 pending、idle、prepare 回调再处理任何挂起的 I/O 回调。如果你在它们当中调用uv_stop()stop_flag被置位uv__backend_timeout()返回 0见 src/unix/core.c于是uv__io_poll不再阻塞等待 I/O反之如果你在 check 回调里调用uv_stop()I/O 早已完成不受影响。uv_stop()的实用价值在于当结果已算出或出错时不必逐个停掉所有 handler直接停掉整个循环即可。示例 docs/code/uvstop/main.c 同时注册了 idle 与 prepare 两个 watcheridle 回调计数到 5 时调用uv_stop()可以看到当前迭代仍会完成的行为#include stdio.h #include uv.h int64_t counter 0; void idle_cb(uv_idle_t *handle) { printf(Idle callback\n); counter; if (counter 5) { uv_stop(uv_default_loop()); printf(uv_stop() called\n); } } void prep_cb(uv_prepare_t *handle) { printf(Prep callback\n); } int main() { uv_idle_t idler; uv_prepare_t prep; uv_idle_init(uv_default_loop(), idler); uv_idle_start(idler, idle_cb); uv_prepare_init(uv_default_loop(), prep); uv_prepare_start(prep, prep_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); return 0; }事件循环的更完整设计I/O 循环各阶段还可继续阅读仓库内的 docs/src/design.rst。后续章节导览一本书的完整地图about.rst与introduction.rst交代了背景而指南的正文从这里才刚刚开始。作为收尾把八章的技术内容在地图上标注出来方便按需深入章节文档路径核心内容基础docs/src/guide/basics.rst事件循环、Hello World、错误处理、Handle/Request 模型文件系统docs/src/guide/filesystem.rstuv_fs_*系列、同步/异步双形态、线程池执行的阻塞操作网络docs/src/guide/networking.rstTCP/UDP 套接字、非阻塞 I/O、DNS线程docs/src/guide/threads.rst线程创建、同步原语进程docs/src/guide/processes.rst子进程、spawn、管道事件循环docs/src/guide/eventloops.rstuv_stop、多循环、循环嵌入工具docs/src/guide/utilities.rst定时器、uv_ref/uv_unref引用计数、其他实用 API例如文件系统章节会告诉你一个关键区别socket 操作使用操作系统提供的非阻塞原语而文件系统操作内部调用阻塞函数但通过线程池调度、在需要应用交互时通知事件循环上的 watcher详见 docs/src/guide/filesystem.rst 开头注释所有uv_fs_*函数都有同步/异步两种形态回调为 NULL 时自动同步执行并阻塞返回值为 libuv 错误码。至此从这本书为什么存在、为谁而写到 libuv 的历史架构、构建方式再到第一个事件循环程序和uv_stop的底层语义你已经拿到了阅读后续所有章节所需的完整上下文。接下来请打开 docs/src/guide/basics.rst 与 docs/src/guide/networking.rst配合 docs/code 目录下的示例逐个编译运行事件驱动编程的手感会很快建立起来。赞分享网络通信异步编程【免费下载链接】libuvCross-platform asynchronous I/O项目地址https://gitcode.com/gh_mirrors/li/libuv点击查看免费下载相关推荐libuv事件循环异步编程的核心引擎libuv事件循环异步编程的核心引擎 本文深入解析了libuv事件循环的11个执行阶段、UV_RUN运行模式的区别、定时器处理机制以及线程安全性与多事件循环的网络通信异步编程libuv 用户指南从事件循环到多进程的跨平台异步 I/O 编程实战libuv 用户指南从事件循环到多进程的跨平台异步 I/O 编程实战 libuv 是一个跨平台的高性能事件驱动 I/O 库为 Windows 与 Unix网络通信异步编程libuv 基础指南事件循环、Handles 与 Requests 编程模型详解libuv 基础指南事件循环、Handles 与 Requests 编程模型详解 导读 本文是 libuv 官方指南的第二篇Basics of libuv网络通信异步编程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考