libxev
libxev 是一个跨平台的事件循环库。它为非阻塞 IO、定时器、信号、事件等提供了统一的事件循环抽象,可在 macOS、Windows、Linux 和 WebAssembly(浏览器和 WASI)上运行。该库使用 Zig 编写,但导出了兼容 C 的 API(这进一步使其与任何可与 C API 通信的语言兼容)。
项目状态:🐲 不稳定,alpha 质量。 在多个平台上的功能列表相当不错,但仍有许多缺失的功能。该项目尚未在实际环境中得到充分测试,还有很多性能优化的低垂果实。目前我也不承诺任何 API 兼容性。如果你需要一个生产就绪、高质量、通用的事件循环实现,请查看 libuv、libev 等。
为什么要开发新的事件循环库? 有几个原因。首先,我认为 Zig 缺少一个在功能上可与 libuv 相媲美的通用事件循环("通用"是这里的关键词)。其次,我想围绕 io_uring 的设计模式构建这样一个库,甚至在其他操作系统原语之上模仿其风格(感谢这篇出色的博客文章)。第三,我想要一个可以构建到 WebAssembly(包括 WASI 和独立环境)的事件循环库,而这并不太适合现有库的 API 风格和目标,除非引入像 Emscripten 这样的重量级工具。不过,开发这个库的主要动机是满足我自己的需求!
特性
跨平台。 支持 Linux(io_uring
和 epoll
)、macOS(kqueue
)、WebAssembly + WASI(poll_oneoff
,线程和非线程运行时)。(Windows 支持已计划,即将推出)
Proactor API。 工作被提交到 libxev 事件循环,调用者会收到工作完成的通知,而不是工作就绪的通知。
零运行时内存分配。 这有助于使运行时性能更可预测,并使 libxev 非常适合嵌入式环境。
定时器、TCP、UDP、文件、进程。 提供高级平台无关的 API,用于与定时器、TCP/UDP 套接字、文件、进程等进行交互。对于不支持异步 IO 的平台,文件操作会自动调度到线程池。
通用线程池(可选)。 你可以创建一个通用线程池,配置其资源利用,并使用它来执行自定义后台任务。某些后端使用线程池来执行没有可靠非阻塞 API 的非阻塞任务(例如使用 kqueue
的本地文件操作)。线程池可以在多个线程和事件循环之间共享,以优化资源利用。
低级和高级 API。 高级 API 是平台无关的,但有一些固定的行为和有限的灵活性。推荐使用高级 API,但低级 API 始终可作为备选方案。低级 API 是特定于平台的,为 libxev 用户提供了一种机制来榨取最大性能。低级 API 在操作系统接口之上提供了恰到好处的抽象,使其更易于使用,同时不会牺牲明显的性能。
树摇(Zig)。 这是 Zig 的一个特性,但对 libxev 这样的库有实质性的好处。Zig 只会包含你实际使用的函数调用和特性。如果你不使用某种特定类型的高级监视器(如 UDP 套接字),那么与该抽象相关的功能根本不会被编译到你的最终二进制文件中。这使得 libxev 可以支持可选的"锦上添花"功能,在某些情况下可能被认为是"臃肿"的,但最终用户不必为此付出代价。
无依赖。 libxev 在运行时除了内置的操作系统 API 外没有其他依赖。C 库依赖于 libc。这使得交叉编译变得非常容易。
路线图
还有许多缺失的功能,我仍然想要添加:
- 管道高级 API
- 信号处理器
- 文件系统事件
- Windows 后端
- 通过外部事件循环(即浏览器)支持独立的 WebAssembly
等等...
性能
还有很多性能改进的空间,我想明确表示我还没有做太多优化工作。尽管如此,性能表现看起来不错。我尝试将许多 libuv 基准测试 移植到使用 libxev API。
在我有更好的环境运行它们之前,我不会发布具体的基准测试结果。作为一个非常广泛的概括,使用 libxev 与其他主要事件循环相比,你不应该注意到明显的速度下降。这可能因功能而异,如果你能在问题中展示真正糟糕的性能,我很有兴趣解决它!
示例
下面的示例展示了使用 Zig 和 C 编写的相同程序,它们都使用 libxev 运行一个 5 秒的定时器。这个例子简单得近乎愚蠢,但旨在传达库的整体感觉,而不是实际的用例。
Zig | C |
|
|
安装(Zig)
**这些说明仅适用于Zig下游用户。**如果你正在使用libxev的C API,请参阅"构建"部分。
此软件包适用于Zig 0.11中引入的Zig包管理器。创建一个如下所示的build.zig.zon
文件:
.{
.name = "my-project",
.version = "0.0.0",
.dependencies = .{
.libxev = .{
.url = "https://github.com/mitchellh/libxev/archive/<git-ref-here>.tar.gz",
.hash = "12208070233b17de6be05e32af096a6760682b48598323234824def41789e993432c",
},
},
}
在你的build.zig
中:
const xev = b.dependency("libxev", .{ .target = target, .optimize = optimize });
exe.addModule("xev", xev.module("xev"));
文档
🚧 文档正在编写中。🚧
目前,文档有三种形式:man页面、示例和代码注释。未来,我计划以网站形式编写详细的指南和API文档,但目前尚不可用。
Man页面
man页面相当详细!xev(7)
将为你提供整个库的概览。xev-zig(7)
和xev-c(7)
分别提供Zig和C API的概览。此外,还有特定API的man页面,如xev_loop_init(3)
。这是目前最好的文档。
浏览man页面有多种方式。最直接友好的方式是在网络浏览器中浏览docs/
目录中的原始man页面源文件。man页面源文件采用类似markdown的语法,所以通过GitHub在浏览器中呈现效果还不错。
另一种方法是运行zig build -Dman-pages
,man页面将在zig-out
中可用。这需要安装scdoc(大多数包管理器中都有)。构建man页面后,你可以按路径渲染它们:
$ man zig-out/share/man/man7/xev.7
最后一种方法是通过你喜欢的包管理器安装libxev(如果可用),这应该会将man页面放入你的man路径中,这样你就可以直接使用man 7 xev
。
示例
examples/
文件夹中提供了示例。示例有C和Zig两种版本,你可以通过文件扩展名区分它们。
要构建示例,请使用以下命令:
$ zig build -Dexample-name=_basic.zig
...
$ zig-out/bin/example-basic
...
-Dexample-name
的值应该是包括扩展名在内的文件名。
代码注释
Zig代码有详细的注释。如果你习惯阅读代码注释,你可以从中获得很多见解。源代码在src/
目录中。
构建
构建需要安装最新的Zig每日构建版。libxev没有其他构建依赖项。
安装后,单独运行zig build install
将构建完整的库,并在zig-out
中输出一个符合FHS标准的目录。你可以使用--prefix
标志自定义输出目录。
测试
libxev有一个庞大且不断增长的测试套件。要运行当前平台的测试:
$ zig build test
...
这将运行当前主机平台支持的所有功能的所有测试。例如,在Linux上,这将同时运行完整的io_uring和epoll测试套件。
你可以为其他平台构建和运行测试,方法是交叉编译测试可执行文件,将其复制到目标机器并执行。例如,下面展示了如何从Linux交叉编译并构建macOS的测试:
$ zig build -Dtarget=aarch64-macos -Dinstall-tests
...
$ file zig-out/bin/xev-test
zig-out/bin/xev-test: Mach-O 64-bit arm64 executable
**WASI是一个特殊情况。**如果你安装了wasmtime,你可以运行WASI的测试:
$ zig build test -Dtarget=wasm32-wasi -Dwasmtime
...