IO 模块文档
本文档详细介绍了 veloq-runtime 的核心 I/O 模块 (src/io)。作为运行时的基石,该模块提供了一套基于 Proactor 模式的高性能异步 I/O 抽象。
1. 概要 (Overview)
src/io 模块不仅仅是文件和网络操作的集合,它定义了 Veloq 运行时的 I/O 交互模型。与 Rust 标准库或 Tokio 中常见的 poll_read / poll_write (Reactor 模型) 不同,Veloq 采用 所有权传递 (Ownership Passing) 的方式进行 I/O。
核心差异在于:
- 传统模型: 借用缓冲区 (
&mut [u8])。这在 io_uring 等异步接口上很难实现零拷贝,因为内核可能会在 Future 被 drop 后继续写入该缓冲区。 - Veloq 模型: 传递缓冲区所有权 (
FixedBuf)。用户将缓冲区交给运行时,I/O 完成后运行时归还缓冲区和结果。
该模块导出了所有必要的组件来构建上层网络协议和文件系统操作。
2. 理念和思路 (Philosophy and Design)
2.1 基于所有权的异步 I/O Trait
在 src/io.rs 中定义了两个核心 Trait:AsyncBufRead 和 AsyncBufWrite。
#![allow(unused)]
fn main() {
pub trait AsyncBufRead {
fn read(&self, buf: FixedBuf) -> impl Future<Output = (io::Result<usize>, FixedBuf)>;
}
}
这种设计强制要求调用者在发起 I/O 时放弃对缓冲区的控制权。这完美契合 io_uring 和 IOCP 的工作方式——一旦操作提交,内存区域必须保持稳定且由内核独占,直到完成信号到达。
2.2 模块化分层
io 模块采用严格的分层架构:
- 顶层 (
io.rs): 定义通用的行为 (Traits) 和入口。 - 操作层 (
op.rs): 定义具体的 I/O 意图(Op<T>,如“读取文件”、“连接 Socket”)。这些定义是严格平台无关的数据载体,实现了从意图到执行的解耦。 - 资源层 (
socket.rs,buffer.rs): 管理 I/O 所需的资源(句柄、内存)。 - 驱动层 (
driver.rs): 处理与操作系统的脏活累活,实现PlatformOp到系统调用的映射。
3. 模块内结构 (Internal Structure)
src/io.rs // 核心入口,定义 AsyncBufRead/AsyncBufWrite Traits
src/io/
├── buffer.rs // 内存管理 (FixedBuf, BufPool)
├── driver.rs // 驱动抽象 (Driver Trait, PlatformDriver)
├── op.rs // 操作定义 (Op<T>, OpSubmitter, Local/Remote Submission)
└── socket.rs // Socket/Handle 抽象
buffer: 提供FixedBuf,这是所有 I/O 操作的数据载体。详见docs/src/io/buffer/README.md。driver: 定义Drivertrait,这是 Reactor/Proactor 的核心接口。详见docs/src/io/driver/README.md。op: 核心操作抽象。Op<T>: 通用数据载体,表示 I/O 意图。不包含任何驱动/运行时引用。OpSubmitter: 统一提交接口。LocalSubmitter: 将Op提交到当前线程的驱动 (submit_local)。RemoteSubmitter: 将Op注入到远程驱动 (submit_remote)。
- 具体操作结构体: 如
ReadFixed,Connect,Open等平台无关结构。
socket: 提供跨平台的句柄封装。
4. 代码详细分析 (Detailed Analysis)
4.1 统一的操作提交模型 (Op<T>)
Veloq 的 Op<T> 设计实现了数据与执行的彻底分离。
- 构建阶段: 用户构建一个
Op::new(ReadFixed { ... })。此时它只是一个纯数据结构,可以在线程间只有移动。 - 提交阶段:
- Local: 使用
submit_local。操作由当前线程的 Driver 直接处理,生命周期和结果直接通过LocalOpFuture 关联。高性能,无跨线程开销。 - Remote: 使用
submit_remote。操作被打包并通过Injector发送到持有资源的 Driver 线程。返回RemoteOpFuture,通过 channel 接收结果。 这种对偶性(Duality)使得同一个 I/O 逻辑可以无缝运行在 Thread-Per-Core 模型(Local)或 Work-Stealing 模型(Remote)下。
- Local: 使用
4.2 AsyncBufRead / AsyncBufWrite
这两个 Trait 是 Veloq I/O 生态的一等公民。
- 设计权衡: 相比于
AsyncRead(poll_read),AsyncBufRead对编译器更友好(无需处理复杂的生命周期),但对用户代码有侵入性(需要管理 Buffer 池)。 - 返回值:
(Result<usize>, FixedBuf)。即使 I/O 失败,缓冲区也必须归还给用户,以便重用或释放。如果设计成只返回Result,那么在错误发生时缓冲区就会泄漏(被 drop 掉),这是不可接受的。
4.3 模块重导出
io.rs 充当了 Facade(门面):
#![allow(unused)]
fn main() {
pub mod buffer;
pub mod driver;
pub mod op;
pub(crate) mod socket; // socket 内部实现细节较多,对外主要暴露类型
}
这种结构隐藏了内部实现的复杂性,用户在使用时通常只需要引入 veoq_runtime::io 即可访问大部分功能。
5. 存在的问题和 TODO (Issues and TODOs)
-
生态兼容性:
- 现有的 Rust 异步生态(Tokio, Hyper 等)严重依赖
AsyncRead/AsyncWrite。Veloq 的AsyncBufRead无法直接与它们互通。 - TODO: 提供适配层 (
Compat),使用内部缓冲区来模拟AsyncRead,但这会引入一次内存拷贝,牺牲部分性能以换取兼容性。
- 现有的 Rust 异步生态(Tokio, Hyper 等)严重依赖
-
Trait 方法的泛化:
- 目前
read接受FixedBuf具体类型。未来可能通过泛型支持任何实现了AsIoSlice的类型,但这会增加 VTable 调度的复杂性。
- 目前
-
Vectored I/O:
- 目前的 Trait 仅支持单个 buffer (
read,write)。对于readv/writev(Scatter/Gather) 的支持尚未在顶层 Trait 中体现。 - TODO: 添加
read_vectored和write_vectored支持。
- 目前的 Trait 仅支持单个 buffer (
6. 未来的方向 (Future Directions)
-
统一流抽象 (Unified Stream Abstraction):
- 构建基于
AsyncBufRead的BufReader和BufWriter等高级工具,提供行读取、按分隔符读取等功能。
- 构建基于
-
Pipeline I/O:
- 探索类似 Linux
splice或sendfile的高级 Trait,允许数据在两个文件描述符之间直接传输,完全绕过用户态缓冲区。
- 探索类似 Linux
-
Completion-based Traits:
- 考虑引入
AsyncReadCom等 Trait,直接返回impl Future,进一步利用 Rust 2024 的async fnin trait 特性(目前 Veloq 手写了 Future 返回值以保证向后兼容或特殊控制)。
- 考虑引入