Rust 异步编程原理:Future、状态机、Waker 与 Executor

Rust 的 async/await 写起来很像同步代码:调用异步函数、等待结果,然后继续向下执行。但它的底层既不会为每个任务创建一个线程,也不会在 await 时阻塞当前线程。 它真正做的事情是:编译器把异步函数转换成一个可以暂停和恢复的状态机,这个状态机实现 Future;Executor 反复调用 poll 推进状态机,Waker 在资源就绪后通知 Executor 再次调度。 本文从一段普通异步代码出发,逐层解释: async fn 返回的到底是什么; await 为什么能暂停函数; 异步代码和状态机是什么关系; Future::poll、Context 和 Waker 分别负责什么; Executor 与 Reactor 如何配合; 为什么 Rust 异步需要 Pin; Tokio 在这套模型中处于什么位置。 本文定位为 《Rust Tokio Runtime 原理与最佳实践》 的前置教程。建议先理解本文中的 Future::poll、状态机、Waker 和 Executor,再继续阅读 Tokio 的调度器、I/O Driver、任务取消与工程实践。 阅读前准备 读者只需要了解 Rust 的基本语法、所有权、枚举和 trait,不要求提前掌握 Tokio。 文中的普通 Rust 代码可以放入一个空项目中实验: cargo new rust-async-basics cd rust-async-basics 涉及 Tokio 的示例可加入依赖: [dependencies] tokio = { version = "1", features = ["full"] } 学习时建议始终追问三个问题: ...

2026年8月25日 · 7 分钟 · Hellokitty

PCIe BAR 深入解析:从设备地址窗口到 DMA 与 GPUDirect Storage

在操作系统驱动、高性能网卡、NVMe SSD 和 GPU 系统中,经常会同时看到 BAR、MMIO、DMA、IOMMU、peer-to-peer DMA、GPUDirect RDMA 和 GPUDirect Storage 等概念。这些名词都与“设备怎样通过 PCIe 交换控制信息和数据”有关,但它们处在不同层次。 最简洁的理解是: BAR:让 CPU 能够定位并访问 PCIe 设备中的寄存器或显存窗口 DMA:让 PCIe 设备能够主动读取或写入系统内存 P2P DMA:让一个 PCIe 设备直接访问另一个 PCIe 设备暴露的地址空间 GDS:利用 DMA、GPU 内存映射和驱动协作,让存储数据尽量直接进入 GPU 显存 本文从 PCIe 地址空间和事务模型出发,解释 BAR 是如何分配和映射的,驱动为什么通过 BAR 下发命令,DMA 地址为什么不能简单等同于物理地址,以及 GPUDirect Storage 如何把这些机制组合成一条高性能数据路径。 一、先建立 PCIe 系统视图 一个典型服务器的 PCIe 拓扑如下: CPU │ Memory Controller │ Host RAM │ Root Complex ┌──────┴──────┐ │ │ PCIe Switch PCIe Endpoint ┌────┴────┐ GPU │ │ NVMe SSD NIC CPU 和内存构成主机侧。Root Complex 把 CPU/内存系统连接到 PCIe fabric。NVMe、网卡和 GPU 通常是 Endpoint。PCIe Switch 用于扩展端口和转发事务。 ...

2026年8月21日 · 10 分钟 · Hellokitty

Rust Tokio 深入解析:运行时原理、并发模型与最佳实践

Rust 语言只定义了 Future、async/await 和 Waker 等异步基础设施,并没有在标准库中提供完整的异步运行时。Tokio 补齐了这一层:它提供任务调度器、网络 I/O 驱动、定时器、异步同步原语、异步文件接口以及阻塞任务隔离机制。 如果把 Rust 异步程序比作一座城市: Future = 等待执行的工作 Executor/Scheduler = 安排工作在哪个线程运行 I/O Driver = 监听 socket 是否就绪 Timer Driver = 管理定时器何时到期 Waker = 通知某个任务可以继续 Tokio = 将上述组件组合成运行时 本文不再重复 Future::poll 和状态机的基础推导,而是聚焦 Tokio 本身:runtime 如何启动,任务如何调度,网络 I/O 为什么不会阻塞线程,以及生产代码中应该怎样处理并发、超时、共享状态、CPU 密集任务和优雅退出。 一、Tokio 提供了什么 一个典型 Tokio 应用依赖: [dependencies] tokio = { version = "1", features = ["full"] } full 适合学习和应用开发,但库作者通常应该只启用实际需要的 feature,缩短编译时间并减少依赖面。 Tokio 的主要组成包括: 组件 作用 Runtime 组合调度器、I/O driver 和 timer driver Task 由 runtime 调度的异步任务 tokio::net TCP、UDP、Unix Socket 等异步网络接口 tokio::time sleep、interval、timeout tokio::sync channel、Mutex、RwLock、Semaphore、Notify tokio::fs 文件系统异步接口 spawn_blocking 将阻塞或 CPU 密集工作移出异步 worker select! 同时等待多个异步分支 需要先建立一个重要认识: ...

2026年8月21日 · 6 分钟 · Hellokitty

Mooncake Transfer Engine 源码阅读(四):TENT 下一代架构如何重构传输引擎

Mooncake 仓库同时存在经典 Transfer Engine 和 TENT(Transfer Engine Next)。TENT 不是简单增加一种 Transport,而是重构了 Segment 生命周期、运行时调度、拓扑选择、QoS、故障转移和插件体系。 阅读版本: Mooncake commit: 777cc7782417b6e554cf7c2d53210d0d8f89f5cc 一、为什么需要下一代引擎 经典 TE 已经支持 RDMA、TCP、NVLink 和多种硬件,但随着后端增加,TransferEngineImpl + MultiTransport + 各 Transport 容易出现几个问题: Segment 生命周期分散在元数据和后端注册逻辑中; 传输选择主要依赖静态协议与局部规则; 多 rail、拥塞、故障和 QoS 难以统一调度; 不同后端各自维护进度线程和资源模型; 新硬件接入需要理解大量经典内部约定; 请求取消、deadline 和 failover 缺少统一运行时。 TENT 将这些能力上移到 Runtime 层。 二、目录结构 TENT 核心位于: tent/src/runtime/ ├── transfer_engine_impl.cpp ├── segment.cpp ├── segment_manager.cpp ├── segment_registry.cpp ├── segment_tracker.cpp ├── transport_loader.cpp ├── transport_selector.cpp ├── progress_worker.cpp ├── admission_queue.cpp ├── qos_contract.cpp ├── receiver_credit.cpp ├── topology.cpp ├── control_plane.cpp └── proxy_manager.cpp 外围包括: ...

2026年8月21日 · 3 分钟 · Hellokitty

Mooncake Transfer Engine 源码阅读(三):RDMA、TCP 与请求完成状态机

Transfer Engine 的公共 API 很统一,但 RDMA 和 TCP 的实现差异很大。RDMA 需要 MR、QP、WR 和 CQ;TCP 需要连接、lane、消息 framing 和接收端主动拷贝。本文沿源码比较两条路径,并分析 Batch 状态如何完成。 阅读版本: Mooncake commit: 777cc7782417b6e554cf7c2d53210d0d8f89f5cc 一、Transport 抽象 Transport 基类统一定义: install / uninstall registerLocalMemory / unregisterLocalMemory submitTransfer getTransferStatus allocateBatchID / freeBatchID 它还定义 TransferRequest、TransferStatus 和内部 BufferEntry。 公共抽象要求每个后端回答三个问题: 本地内存如何准备为可传输状态; 请求怎样排队和执行; 如何查询每个 task 的最终状态和字节数。 具体连接模型不属于公共 API。 二、RDMA 初始化 RDMA 后端主要位于: rdma_transport.cpp rdma_context.cpp rdma_endpoint.cpp endpoint_store.cpp worker_pool.cpp 安装阶段通常完成: 枚举 RDMA devices / ports / GID -> 创建每张 HCA 的 context -> 建立 PD、CQ 等资源 -> 启动 worker / poller -> 发布 NIC endpoint metadata -> 准备 endpoint store Mooncake 支持多 HCA、多端口和 GPU 内存,因此一个逻辑请求可能被切到多个 rail 上并行发送。 ...

2026年8月21日 · 3 分钟 · Hellokitty

Mooncake Transfer Engine 源码阅读(二):Segment、内存注册与元数据服务

Mooncake Transfer Engine 的核心抽象不是“远端指针”,而是 Segment + BufferDesc + Metadata。应用注册本地内存后,TE 将地址范围、设备位置和传输协议发布为 Segment 描述,其他节点才能定位并建立连接。 阅读版本: Mooncake commit: 777cc7782417b6e554cf7c2d53210d0d8f89f5cc 一、为什么不能直接发送指针 一个进程中的地址: 0x7f12... 在另一个进程中通常没有意义。即使两台机器都使用相同数值,它们也不指向同一块物理内存。 高性能传输还需要额外信息: 这是 CPU DRAM 还是 GPU VRAM; 对应哪张 GPU; 是否注册成 RDMA MR; rkey 和网卡 endpoint 是什么; 是否能用 NVLink、GPU IPC 或 CXL; 节点当前是否存活; 地址区间是否已经注销。 因此 TE 把地址空间包装成 Segment,并通过元数据服务交换描述。 二、SegmentDesc TransferMetadata::SegmentDesc 是远端发现的中心结构,主要承载: Segment ID Segment 名称 协议或协议集合 本机 RPC / endpoint 信息 BufferDesc 列表 设备与拓扑信息 后端扩展元数据 可以把一个 Segment 理解成某个 TE 实例公开的可传输地址空间: Segment: decode-node-7 ├── Buffer A: CPU metadata pool, protocol=rdma ├── Buffer B: GPU KV pool, protocol=rdma └── Buffer C: GPU KV pool, protocol=hip 同一块 GPU buffer 可以被多个后端注册,从而同时支持: ...

2026年8月21日 · 3 分钟 · Hellokitty

Mooncake Transfer Engine 源码阅读(一):统一传输 API 与整体架构

Mooncake Transfer Engine(简称 TE)是 Mooncake 数据平面的基础组件。它不负责决定 KV Cache 应该放在哪里,而是提供统一接口,把一段本地内存搬到远端 Segment,或者从远端 Segment 读取到本地内存。 本文基于 Mooncake 官方仓库: commit: 777cc7782417b6e554cf7c2d53210d0d8f89f5cc commit date: 2026-08-21 当前仓库同时保留经典 Transfer Engine 和下一代 TENT。前三篇先阅读经典实现,第四篇再分析 TENT 如何重构控制面、调度和传输后端。 一、源码布局 核心目录为 mooncake-transfer-engine/: 路径 作用 include/transfer_engine.h 对外 C++ API include/transfer_engine_impl.h 经典实现内部接口 include/transport/transport.h Transport 抽象、请求和状态 include/transfer_metadata.h Segment、Buffer 与元数据接口 src/transfer_engine.cpp 公共 API 转发层,同时兼容经典 TE 与 TENT src/transfer_engine_impl.cpp 初始化、内存注册、Segment 与批次管理 src/multi_transport.cpp 安装和选择具体 Transport src/transport/ RDMA、TCP、NVLink、NVMe-oF、EFA 等后端 tent/ Transfer Engine Next 实现 TE 的经典架构可以概括为: 应用 / Mooncake Store │ ▼ TransferEngine │ ▼ TransferEngineImpl ┌────┼──────────────┐ ▼ ▼ ▼ Metadata MultiTransport Batch 生命周期 │ ┌──────┼───────────────┐ ▼ ▼ ▼ ▼ RDMA TCP NVLink NVMe-oF ... 二、公共 API 是一层门面 TransferEngine 类本身很薄,大多数函数转发给 TransferEngineImpl: ...

2026年8月21日 · 3 分钟 · Hellokitty

nvidia-fs 源码阅读(三):文件 I/O、批处理、完成路径与诊断

前两篇分别介绍了 nvidia-fs 的模块结构,以及 GPU virtual address 到 peer DMA address 的映射。本文沿一次 cuFileRead 对应的内核路径,分析文件 I/O 如何提交、完成和清理,并介绍 batch、稀疏文件、RDMA 与 /proc 诊断接口。 阅读版本: commit: 328d1d8cce1175c013720985c30e123e9a35242c GDS_VERSION: 2.29.4 一、I/O 入口 nvfs_ioctl() 接收: NVFS_IOCTL_READ NVFS_IOCTL_WRITE NVFS_IOCTL_BATCH_IO 单次读写共用两阶段结构: nvfs_io_init(op, ioargs) -> 验证并构造 nvfs_io nvfs_io_start_op(nvfsio) -> 向目标文件提交真正 I/O 分成两步的意义在于:参数验证、对象引用和资源分配都应在进入异步 I/O 前完成。一旦请求提交,完成回调可能很快发生,初始化不完整会造成竞态。 二、nvfs_io_init 做了什么 nvfs_io_init() 是用户参数到内核 I/O 对象的转换层,主要工作包括: 根据文件描述符取得目标 struct file; 检查读写权限; 查找已经注册的 GPU buffer/mgroup; 验证 GPU buffer offset、文件 offset 和长度; 建立影子 page 对应的 iov 或迭代器; 初始化 kiocb、完成函数和统计字段; 为同步、异步和特殊文件系统路径设置标志。 源码还会检查文件系统类型、direct I/O 条件和文件权限。写操作比读操作更复杂,因为它可能涉及文件扩展、页缓存一致性及磁盘空间预分配。 ...

2026年8月21日 · 3 分钟 · Hellokitty

nvidia-fs 源码阅读(二):GPU 内存注册、影子页与 DMA 映射

上一篇从 nvfs_init()、字符设备和 ioctl 看到了 nvidia-fs 的外部形态。本文进入 GDS direct path 的核心:怎样把用户分配的 GPU virtual address 变成存储设备能够 DMA 的地址,同时又让 Linux 文件 I/O 栈能够携带它。 阅读版本仍为 NVIDIA gds-nvidia-fs: commit: 328d1d8cce1175c013720985c30e123e9a35242c GDS_VERSION: 2.29.4 一、问题本质 普通块 I/O 最终围绕 bio_vec、struct page、scatterlist 和 DMA mapping 展开。但 cudaMalloc 得到的是 GPU 虚拟地址,对 Linux 页缓存和块层来说,它并不是普通 CPU 内存页。 驱动需要解决三次“翻译”: GPU virtual address -> NVIDIA P2P page table 中的 GPU physical pages -> Linux I/O 栈可携带的影子 struct page -> 某个存储 PCIe 设备可使用的 DMA address 三个地址空间不能混为一谈: ...

2026年8月21日 · 4 分钟 · Hellokitty

nvidia-fs 源码阅读(一):模块初始化、设备接口与整体架构

nvidia-fs 是 NVIDIA GPUDirect Storage(GDS)的 Linux 内核模块。它不是一种文件系统,而是连接 libcufile、NVIDIA GPU 驱动、Linux 文件 I/O 和支持 GPUDirect 的存储驱动的一层内核协调组件。 这组文章阅读 NVIDIA 官方 gds-nvidia-fs 仓库,采用的版本为: commit: 328d1d8cce1175c013720985c30e123e9a35242c GDS_VERSION: 2.29.4 commit date: 2026-06-01 系列分为三篇: 模块初始化、设备接口与整体架构; GPU 内存注册、页表与 DMA 映射; 文件 I/O、批量请求、完成路径与可观测性。 本文先回答一个问题:加载 nvidia_fs.ko 后,内核里究竟多了什么? 一、源码目录 核心代码都位于 src/: 文件 职责 nvfs-core.c/.h 字符设备、ioctl、GPU 内存注册和文件 I/O 主流程 nvfs-mod.c 与 NVMe、RDMA 等外部模块动态注册 DMA 回调 nvfs-mmap.c/.h GPU 页对应的影子 struct page 和 mmap 管理 nvfs-dma.c/.h block request 到 GPU DMA 地址的映射 nvfs-batch.c/.h 批量 I/O 提交与完成 nvfs-rdma.c/.h RDMA 注册信息管理 nvfs-pci.c/.h GPU 与存储设备的 PCIe 拓扑、距离和亲和性 nvfs-proc.c、nvfs-stat.c /proc 配置、统计和诊断接口 nvfs-kernel-interface.c 不同 Linux 内核版本的兼容封装 主线集中在 nvfs-core.c,但真正的数据直达能力是多个文件共同完成的。 ...

2026年8月21日 · 3 分钟 · Hellokitty