Redox Ring Buffer 项目总结

Lobsters Hottest 新闻

摘要

Redox 项目团队总结了 NLnet 资助的 Ring Buffer 工作成果:在系统任务层实现了类似 io_uring 的 Redox Rings 接口,支持应用与 scheme 之间零拷贝的异步 I/O 通信,并包含驱动异步执行器与性能优化。

<p><a href="https://lobste.rs/s/brdwr2/redox_ring_buffers_project_summary">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/10/29 03:36

# Ring Buffers 项目总结 - Redox 来源: https://www.redox-os.org/news/nlnet-rb-summary/ ##### 作者: Ibuki Omatsu, Anhad Singh, Jacob Lorentzon, Ron Williams,发布于2026年10月25日,星期日 去年,Redox 团队获得了一笔资助 (https://nlnet.nl/project/RedoxOS-ringbuffer/),用于在系统任务层面开发类似 io\_uring 的 Ring Buffer 支持,这是实现应用与大多数系统服务之间环形缓冲通信的第一步。此阶段工作已于九月完成,所有工作均已就位。 我们要感谢NLnet Foundation (https://nlnet.nl/)、NGI Zero Commons Fund 以及 Horizon Europe 的慷慨支持。NGI Zero Commons Fund 即将结束,但 NLnet 现在接受来自新基金的资助申请,包括Restack (https://nlnet.nl/restack/) 和Code Supply (https://nlnet.nl/codesupply/),以及一些较小的主题基金 (https://nlnet.nl/themes/)。请在2026年11月3日前申请资助! (https://nlnet.nl/news/2026/20260903-call.html)(之后每两个月开放一次)。 团队的工作非常出色,我要向 Ibuki、Anhad 和 Jacob 表示祝贺,感谢他们的付出和努力。 除了为支持该功能的系统服务和驱动程序实现了 Ring Buffer 机制外,还完成了面向应用的类 `io\_uring` 和 `liburing` API 并进行了演示。在支持库方面也有大量工作,包括为驱动程序开发的异步执行器。 在各种性能改进方面也有一些令人兴奋的工作,使应用与其系统服务之间的切换速度大幅提升。但技术细节还是让团队来向大家介绍。 – Ron Williams ## Ring Buffers 概览 – Anhad Singh 历史上,Linux 上的异步 I/O 是通过内核的原生 AIO 接口完成的。AIO 主要适用于无缓冲(`O\_DIRECT`)*文件* I/O,带缓冲的请求则会静默阻塞¹ (https://www.redox-os.org/news/nlnet-rb-summary/#fn:1)。开发者还对其复杂性和性能提出了担忧¹ (https://www.redox-os.org/news/nlnet-rb-summary/#fn:1)² (https://www.redox-os.org/news/nlnet-rb-summary/#fn:2)。此外还有 POSIX AIO,但开发者通常认为其实现质量不佳且速度缓慢¹ (https://www.redox-os.org/news/nlnet-rb-summary/#fn:1)。为了解决这些问题,曾提出过多种替代异步机制,如fibrils (https://lwn.net/Articles/219954/) 和syslets/threadlets (https://lwn.net/Articles/223899/),但均未被合并到 Linux 主线内核。2019 年初,Jens Axboe (https://github.com/axboe) 提出并开发了 io\_uring 接口,旨在设计为高效、可扩展且"易于使用,难以误用"。在早期,io\_uring 确实存在一些安全问题和内存泄漏,但这些问题后来都已解决³ (https://www.redox-os.org/news/nlnet-rb-summary/#fn:3)。它于 Linux 5.1 合并,如今已被广泛使用。 Linux 的 io\_uring 接口允许用户向内核提交一个或多个 I/O 操作,这些操作随后以异步方式处理,不会阻塞用户空间进程。Redox 作为微内核,在用户空间的 *scheme* 进程中处理 I/O,而非在内核中。每个 *文件描述符* 都存储了指向其 scheme 的句柄,因此对该文件的 I/O 系统调用可以将请求转发给 scheme。本项目开发的异步 I/O 接口名为 Redox Rings,使用了与 io\_uring 类似的机制。然而,与用于向内核提交操作的 io\_uring 不同,Redox Rings 接口供应用直接向相应的 scheme 异步提交请求,而不阻塞应用。此外,该接口允许在 scheme 与应用之间使用独立的共享内存区域(可请求 DMA 能力的内存)发出 I/O 请求,无需任何内存复制。 为了可移植性,Redox Rings API 的设计与liburing (https://github.com/axboe/liburing) 相似,也可用于通用进程间通信。例如,它被用于 RedoxFS 文件系统服务与 NVMed(存储驱动程序)之间的通信。该 API 的加入使 Redox 的多个部分获得了显著的性能提升! ### 术语 环形缓冲区是基于数组实现的循环队列。该接口使用两个在应用和 scheme 之间共享的环形缓冲区: - SQ:*提交队列*(submission queue),应用在其中写入请求,scheme 在其中读取请求 - CQ:*完成队列*(completion queue),scheme 在其中写入响应,应用在其中读取响应 - SQE / CQE:*提交队列条目*(submission queue entry)/*完成队列条目*(completion queue entry),分别指 SQ 和 CQ 中的条目 - 缓冲池:一块在应用和 scheme 之间共享的连续内存区域,数据缓冲区从中分配 - 数据缓冲区:对于一个请求,目前支持单个数据缓冲区,它是连续缓冲池的一个切片,由偏移量和长度定义 由于每个队列恰好有一个生产者和一个消费者,每个索引只会被一方修改。这消除了使用共享锁机制的开销。 ## "redox-ring API" – Ibuki Omatsu 我们为 Redox OS 引入了首个环形缓冲区实现。与 Linux 的 io\_uring 不同,提交队列(SQ)和完成队列(CQ)完全由用户空间进程共享,内核从不访问它们。 `redox-rings` 是一个用于用户空间进程间环形缓冲通信的新 crate,同时也为 FFI 用例提供了数据结构。它使用 futex 作为主要同步机制,并可选择支持管道文件描述符。 基于其 FFI 支持,我们在 relibc 中引入了"redox-ring API",灵感来源于 Linux 的 liburing。 liburing 是一个用于 Linux io\_uring 的库,提供了设置和销毁 io\_uring 实例的辅助函数,以及用于提交 I/O 请求的简化接口。 类似地,redox-ring API 允许应用使用自定义协议与支持环形缓冲区的守护进程进行通信。 ### 关键函数 该 API 提供了管理环形缓冲区、共享内存和请求的函数。 - `redox_ring_queue_init_with_path()` 初始化到守护进程的环形缓冲区连接。 - `redox_ring_alloc_buf()` 和 `redox_ring_alloc_sized()` 从共享内存池中分配缓冲区。 - `redox_ring_get_sqe()` 获取一个提交队列条目(SQE)用于准备请求。 - `redox_ring_submit()` 将待处理的请求提交给守护进程。 - `redox_ring_wait_cqe()` 等待完成队列条目(CQE),如果没有可用条目则阻塞。 - `redox_ring_cqe_seen()` 将 CQE 标记为已处理。 该 API 还提供了注册文件描述符、释放已分配缓冲区以及销毁环形缓冲区实例的函数。 – Ibuki Omatsu `redox-rings` 实现使用共享内存在应用和守护进程之间交换 SQE、CQE 和 I/O 数据。 在初始化过程中,SQ、CQ 和数据缓冲池通过 `fmap` 系统调用并使用 `MAP_SHARED` 标志映射到应用的地址空间中。这使得双方可以访问同一内存,无需在进程之间复制 I/O 数据。 ### 缓冲区分配 redox-ring API 支持两种缓冲区分配策略: - 固定大小分配:使用一个简单的分配器,将共享内存池划分为固定大小的块,并维护一个可用偏移量列表。 - 动态分配:使用redox-buffer-pool (https://gitlab.redox-os.org/redox-os/redox-buffer-pool) crate 来分配不同大小和对齐方式的缓冲区。该分配器使用 B 树管理空闲和已占用的内存区域。 两种分配策略都返回已分配缓冲区的指针及其在共享内存池中的偏移量。 一旦操作完成,应用可以释放其缓冲区,使内存可以被重用。 ## 会话创建与管理 – Anhad Singh `redox_ring_queue_init_with_path` 函数打开 scheme 环形缓冲区句柄,并使用 `SYS_CALL` 系统调用以给定参数建立环形缓冲区会话。它返回 SQ、CQ 和共享缓冲池的文件描述符,这些将被映射到应用的地址空间中。应用随后可以直接使用这些队列提交请求并接收完成通知。在 scheme 端,工作线程等待 SQ 中的条目,异步处理它们并在完成时通知应用。 ## 示例:写入 RamFS – Ibuki Omatsu 我们为 RamFS 和 NVMed 添加了环形缓冲区支持。我们还添加了通过 redox-ring API 与这些存储守护进程通信的测试。 例如,向 RamFS 写入数据涉及以下步骤: 1. 打开目标文件,并使用 `redox_ring_queue_init_with_path()` 初始化到 RamFS 的环形缓冲区实例。 2. 注册文件描述符,以便后续请求可以通过索引引用它。 3. 分配一个共享内存缓冲区并将数据写入其中。 4. 获取一个 SQE 并指定写操作、文件索引、缓冲区偏移量、数据长度和文件偏移量。 5. 使用 `redox_ring_submit()` 提交准备好的 SQE。可以一次批量提交多个请求。 6. 等待 CQE,检查结果,并释放与已完成请求关联的缓冲区。 所有操作完成后,应用销毁环形缓冲区连接。 ## 异步执行器 – Anhad Singh 我们为设备驱动程序开发了一个异步执行器,采用每个核心架构对应一个中断向量和一对 SQ/CQ 的设计。目前使用单核心,每核心多线程支持正在进行中。此外,我们更新了 NVMe 驱动程序以使用异步执行器,预计 virtio 等更多设备将跟进。 使用该执行器,支持 Redox Rings 的 scheme 可以在各自的异步任务中处理每个 I/O 请求,并为应用与 scheme 之间的每个会话生成一个工作线程。在销毁时,工作线程会被停止,待处理的 I/O 操作被取消,共享资源被清理。 Ring Buffers 基准测试 **图 1:**`redox_syscall` 与 `redox_ring` 基准测试的对比,显示 `redox_ring` 快了 14-15 倍。`redox_syscall` 基准测试通过同步系统调用执行 I/O,而 `redox_ring` 基准测试使用新的 Redox Ring 接口。 ## 内核和 IPC 优化 – Jacob Lorentzon 虽然 IPC 在 Redox 上多年来已有显著改善,但它仍然相当慢,需要优化,因为环形缓冲区只能在一定程度上抵消这部分开销。这是我们改进系统性能的重点工作。 ### 基础设施 Redox 的 `acid` 测试套件已扩展,增加了更多有针对性的基准测试来测量 IPC 延迟,特别是 `acid ipc_latency_simultaneous`,以及 `acid heavy_forking`。还增加了重复测量和收集更完善统计数据的支持(尽管功能仍不如 `hyperfine` 完整),使得更容易判断指标变化是否具有统计显著性以及变化幅度。 ### 各项重构 通过内核重构实现了各种个位数百分比的加速,例如消除冗余锁、哈希表查找,以及添加类似 seL4"回复-等待"(reply-wait)的低开销 IPC 调用。 ### 无向量寄存器的快速路径切换 从火焰图中发现的另一个开销来源是 `XSAVE` 和 `XRSTOR`,在测试的 AMD Ryzen 9 5950X 上合计约数百个周期。这些指令保存/恢复向量寄存器,在测试的 CPU 上约 600 字节,在其他支持 AVX2(仅用户空间使用)的 CPU 上可能类似。 这里做的优化是:*如果调用者和被调用者都是通过系统调用进入内核的*,内核会将向量寄存器清零,而不是保存/恢复(必要时使用 `vzeroall`、`ldmxcsr` 等指令来保证正确性)。 上下文切换可能由抢占(即定时器中断)触发,也可能从系统调用自愿触发。在 Redox 弃用其系统调用 ABI、转而优先使用 `relibc` 提供的 `libredox` 符号之后,所有系统调用都会先经过一次函数调用。由于 System V ABI 在*所有函数调用中*都会破坏所有向量寄存器,因此将 Redox 的系统调用 ABI 也定义为如此几乎是零成本的。幸运的是,`relibc` 生成的汇编代码几乎完全相同。 ### 直接切换 IPC 过程中的一个主要瓶颈是调用调度器,这从各种火焰图中可以看出。调度器需要访问多个数据结构,主要是 `BTreeMap`(占了开销的很大一部分),还需要获取大量锁,通常会阻塞。 直接上下文切换在kernel MR 694 (https://gitlab.redox-os.org/redox-os/kernel/-/merge_requests/694) 中实现,使 IPC 延迟降低了 15-20%。它将*当前上下文*(引用)分离为*已调度上下文*(scheduled context)和*正在运行上下文*(running context),前者是拥有当前时间片的上下文,后者是其寄存器/内存/文件表当前处于激活状态的上下文。当未发生直接切换时,两者始终重合。因此,调用 scheme 的客户端进程(通常使用我们的 `SYS_CALL` 系统调用)可以完全绕过调度器,直接切换到 scheme(如果就绪),内核随后会在 scheme 让出时优先返回*已调度上下文*。这模拟了单体内核中复杂系统调用的处理方式,并且应该也更加缓存友好,还有其他效率优势。 ### 大页 内核之前只支持 4K 页。在非 x86 平台和所有用户空间场景中仍然如此,但现在内核的物理内存映射(physmap)在可能的情况下会以 2M 大页映射。因此,TLB 使用量理论上最多可减少 512 倍(针对重度 CoW 场景),在实践中,`acid heavy_forking` 测试中获得的最高 L1 DTLB 未命中率降低了 -75% (https://gitlab.redox-os.org/redox-os/kernel/-/merge_requests/711)。 ## 未来改进方向 – Jacob Lorentzon IPC 往返延迟已从约 5800 个周期降低到约 2700 个周期,但与 seL4 等快速微内核相比仍然较高。额外的优化方向包括减少锁竞争、降低系统调用进入开销,但主要是简化 IPC 原语并移除可能不必要的部分,例如请求缓冲区。虽然自动页面借用(在 `UserScheme` 中)对非传统应用不太适用,但它也是开销的一个主要来源,在类似 `dd bs=4k if=/dev/zero of=/dev/null` 的基准测试中,每次调用的开销至少约 32000 个周期。然而,虽然现有基准测试作为延迟的近似下界很有用,但还需要更多研究来确定在更真实的工作负载下(无论是否使用用户空间环形缓冲区),开销的具体分布情况。

相似文章

Redox本月回顾 - 2026年8月

Lobsters Hottest

2026年8月,Redox操作系统带来了重大更新,包括ARM64多核支持、用于I/O性能提升14-15倍的环形缓冲区通信、NUMA实现,以及QEMU兼容性改进。

RSoC 2026:Redox OS 中的 EEVDF

Lobsters Hottest

Redox OS 实现了 EEVDF 调度器,替换了 DWRR,显著提升了公平性、上下文切换时间和吞吐量。

探索使用 io_uring 的自动缓冲区管理

Lobsters Hottest

本文详细介绍了在 UringMachine(一个用于异步 I/O 的 Ruby gem)中使用 io_uring 的缓冲区环实现自动缓冲区管理。它解释了缓冲区环如何通过允许内核使用应用程序提供的缓冲区来实现高效的多重读取/接收操作。

魔法缓冲区与io_uring注册缓冲区

Hacker News Top

本文探讨了如何使用mmap创建魔法环形缓冲区,其中两个连续的虚拟内存区域映射同一物理内存,并测试其与io_uring注册缓冲区的兼容性,发现其按预期工作。