Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:AsyncBufReadAsyncBufWrite

#![allow(unused)]
fn main() {
pub trait AsyncBufRead {
    fn read(&self, buf: FixedBuf) -> impl Future<Output = (io::Result<usize>, FixedBuf)>;
}
}

这种设计强制要求调用者在发起 I/O 时放弃对缓冲区的控制权。这完美契合 io_uringIOCP 的工作方式——一旦操作提交,内存区域必须保持稳定且由内核独占,直到完成信号到达。

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: 定义 Driver trait,这是 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 直接处理,生命周期和结果直接通过 LocalOp Future 关联。高性能,无跨线程开销。
    • Remote: 使用 submit_remote。操作被打包并通过 Injector 发送到持有资源的 Driver 线程。返回 RemoteOpFuture,通过 channel 接收结果。 这种对偶性(Duality)使得同一个 I/O 逻辑可以无缝运行在 Thread-Per-Core 模型(Local)或 Work-Stealing 模型(Remote)下。

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)

  1. 生态兼容性:

    • 现有的 Rust 异步生态(Tokio, Hyper 等)严重依赖 AsyncRead/AsyncWrite。Veloq 的 AsyncBufRead 无法直接与它们互通。
    • TODO: 提供适配层 (Compat),使用内部缓冲区来模拟 AsyncRead,但这会引入一次内存拷贝,牺牲部分性能以换取兼容性。
  2. Trait 方法的泛化:

    • 目前 read 接受 FixedBuf 具体类型。未来可能通过泛型支持任何实现了 AsIoSlice 的类型,但这会增加 VTable 调度的复杂性。
  3. Vectored I/O:

    • 目前的 Trait 仅支持单个 buffer (read, write)。对于 readv/writev (Scatter/Gather) 的支持尚未在顶层 Trait 中体现。
    • TODO: 添加 read_vectoredwrite_vectored 支持。

6. 未来的方向 (Future Directions)

  1. 统一流抽象 (Unified Stream Abstraction):

    • 构建基于 AsyncBufReadBufReaderBufWriter 等高级工具,提供行读取、按分隔符读取等功能。
  2. Pipeline I/O:

    • 探索类似 Linux splicesendfile 的高级 Trait,允许数据在两个文件描述符之间直接传输,完全绕过用户态缓冲区。
  3. Completion-based Traits:

    • 考虑引入 AsyncReadCom 等 Trait,直接返回 impl Future,进一步利用 Rust 2024 的 async fn in trait 特性(目前 Veloq 手写了 Future 返回值以保证向后兼容或特殊控制)。