> For the complete documentation index, see [llms.txt](https://man.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://man.bsdcn.org/man9/buf.9.md).

# buf(9)

`buf` — FreeBSD VM 系统中使用的内核缓冲区 I/O 方案

## 名称

`buf`

## 描述

内核实现了缓冲区缓存的 KVM 抽象，允许将可能分散的 vm\_page 映射到连续的 KVM 中，供（主要是文件系统）设备和设备 I/O 使用。此抽象支持从 `DEV_BSIZE`（通常为 512）到数页或更多的块大小。它还支持相对原始的字节粒度有效范围和脏范围，目前硬编码用于 NFS。实现 VM 缓冲区抽象的代码主要集中于 FreeBSD 源码树的 `sys/kern/vfs_bio.c` 中。

处理缓冲区指针（`struct buf`）时需要记住的最重要事项之一是，底层页面直接从缓冲区缓存映射。在该方案本身中不会发生数据复制，尽管某些文件系统（如 UFS）在处理文件片段时确实需要少量复制。需要记住的第二重要事项是，由于底层页面映射，buf 中的 `b_data` 基指针始终是 *页* 对齐的，而非 *块* 对齐的。当有一个表示某个 `b_offset` 和 `b_size` 的 VM 缓冲区时，缓冲区的实际起始位置是 `b_data + (b_offset & PAGE_MASK)`，而不只是 `b_data`。最后，VM 系统的核心缓冲区缓存以 `DEV_BSIZE` 块为单位支持页面有效位和脏位（`m->valid`、`m->dirty`）。因此，硬件页大小为 4096 字节的平台有 8 个有效位和 8 个脏位。这些位通常根据支持页面的设备块大小成组设置和清除。整页的位通常使用 `VM_PAGE_BITS_ALL` 位掩码（即，如果硬件页大小为 4096，则为 0xFF）引用。

VM 缓冲区还跟踪字节粒度的脏范围和有效范围。此功能通常仅由 NFS 子系统使用。实际上，我不确定为什么还要使用它，因为 VM 缓冲区内已有 `DEV_BSIZE` 有效/脏粒度。如果缓冲区脏操作创建了一个"空洞"，脏范围将扩展以覆盖该空洞。如果缓冲区验证操作创建了一个"空洞"，字节粒度的有效范围将保持不变，不会考虑新的扩展。因此，整个字节粒度抽象被视为一个糟糕的权宜之计，如果能完全摆脱它会更好。

VM 缓冲区能够将底层 VM 缓存页映射到 KVM 中，以允许内核直接操作与（`vnode`、`b_offset`、`b_size`）关联的数据。内核通常在不再需要 VM 缓冲区时立即取消映射，但通常会保持 `struct buf` 结构实例化，甚至保持 `bp->b_pages` 数组实例化，尽管已将它们从 KVM 取消映射。如果组成 VM 缓冲区的页面即将进行 I/O，系统通常会将其从 KVM 取消映射，并用一个名为 bogus\_page 的占位符替换 `b_pages[]` 数组中的页面。占位符强制任何引用相关 `struct buf` 的内核子系统重新查找关联的页面。我相信使用占位符技巧是为了允许复杂的设备（如文件系统设备）重新映射底层页面，以便处理例如将文件片段重新映射到文件块的情况。

VM 缓冲区用于跟踪内核中的 I/O 操作。不幸的是，I/O 实现也有些权宜之计，因为内核想在将 I/O 排队到 VFS 设备时就清除底层页面的脏位，而不是在实际启动物理 I/O 时。这会在使用延迟写的文件系统设备中造成混乱，因为最终会出现标记为干净但实际仍然脏的页面。如果不小心处理，这些页面可能会被丢弃！实际上，直到 FreeBSD 2.2.8 和 FreeBSD 3.0 发布才修复了与此权宜之计相关的许多严重错误。内核使用实例化的 VM 缓冲区（即 `struct buf`）来占位处于此特殊状态的页面。缓冲区通常标记为 `B_DELWRI`。当设备不再需要缓冲区时，通常将其标记为 `B_RELBUF`。由于底层页面被标记为干净，`B_DELWRI|B_RELBUF` 组合必须被解释为缓冲区实际仍然脏，必须先写入其后备存储然后才能真正释放。在未设置 `B_DELWRI` 的情况下，底层脏页仍然正确标记为脏，缓冲区可以完全释放而不会丢失该干净/脏状态信息。（XXX 我们是否需要检查与此情况相关的其他标志？？？）

内核保留一部分 KVM 空间来保存 VM 缓冲区的数据映射。即使这是虚拟空间（因为缓冲区从缓冲区缓存映射），也不能使其任意大，因为实例化的 VM 缓冲区（`struct buf`）会阻止其在缓冲区缓存中的底层页面被释放。这会使分页系统的工作复杂化。

## 历史

`buf` 手册页最初由 Matthew Dillon 编写，首次出现于 FreeBSD 3.1（1998 年 12 月）。
