---
title: ZFS — 不是内核的一部分，但绕不开
url: https://doc.liz6.com/linux-kernel/03-file-system/05-ZFS
locale: zh
area: linux-kernel
tags:
- linux-kernel
- 文件系统
date: 2026-06-30
modified: 2026-07-11
description: '覆盖: ZFS 架构 (ZPL/DMU/SPA/ZIL/ARC) → COW 事务模型 → 与 Linux VFS 的集成 (SPL) → 与 Btrfs/bcachefs 的对比 → 许可证问题'
---

# ZFS — 不是内核的一部分，但绕不开

> 覆盖: ZFS 架构 (ZPL/DMU/SPA/ZIL/ARC) → COW 事务模型 → 与 Linux VFS 的集成 (SPL) → 与 Btrfs/bcachefs 的对比 → 许可证问题

## 为什么 ZFS 不在 Linux 内核中

ZFS 使用 CDDL 许可证，与 Linux 的 GPLv2 不兼容。Sun/Oracle 选择不双许可，导致 ZFS 永远不能合入 Linux 主线。

**解决方案**: OpenZFS 以 **内核模块** 的形式提供，通过 DKMS 编译安装。CDDL 模块与 GPL 内核之间的接口通过 SPL (Solaris Porting Layer) 抽象。许可证争议集中在"是否构成衍生作品"——这是一个法律灰色地带，所以各发行版的处理方式不同：
- Ubuntu: 直接打包了 `zfs-dkms`
- Debian: contrib 仓库中提供
- Fedora/RHEL: 不官方支持（用户自行编译）

---

## ZFS 架构栈

<svg viewBox="0 0 720 420" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="ZFS 内核模块架构:从用户空间到 VDEV 物理设备抽象的调用层次">
  <defs>
    <marker id="zfs-arch-arrow" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker>
  </defs>
  <rect width="720" height="420" fill="#ffffff"/>
  <text x="360" y="26" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">ZFS 架构栈:用户空间到物理设备的调用层次</text>

  <rect x="190" y="42" width="340" height="32" rx="6" fill="#e2e8f0"/>
  <text x="360" y="63" text-anchor="middle" font-size="12" font-weight="700" fill="#334155">用户空间 — zfs(1) / zpool(1) / libzfs</text>

  <line x1="360" y1="74" x2="360" y2="88" stroke="#475569" stroke-width="1.6" marker-end="url(#zfs-arch-arrow)"/>
  <text x="360" y="100" text-anchor="middle" font-size="12" font-weight="700" fill="#1f2933">内核 (OpenZFS module)</text>

  <rect x="50" y="108" width="620" height="306" rx="10" fill="none" stroke="#cbd5e1" stroke-dasharray="4 3"/>

  <rect x="64" y="118" width="592" height="44" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="76" y="136" font-size="12" font-weight="700" fill="#3730a3">ZPL(ZFS POSIX Layer) — VFS 接口(i_op / f_op 等)</text>
  <text x="76" y="153" font-size="10" fill="#4f46e5">zfs_znode → 对应 inode → zfs_vnode_ops → VFS file_operations</text>

  <rect x="64" y="168" width="592" height="44" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="76" y="186" font-size="12" font-weight="700" fill="#3730a3">DMU(Data Management Unit) — 事务对象到 block 的映射</text>
  <text x="76" y="203" font-size="10" fill="#4f46e5">dnode→对象(文件/目录/属性) · dmu_tx→事务 · dnode→block pointer tree(间接块)</text>

  <rect x="64" y="218" width="592" height="44" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="76" y="236" font-size="12" font-weight="700" fill="#115e59">ZIL(ZFS Intent Log) — 同步写 journal</text>
  <text x="76" y="253" font-size="10" fill="#0f766e">zil_commit() → 写入 ZIL 设备(SSD/HDD 分区),加速 fsync(同步写不走大事务)</text>

  <rect x="64" y="268" width="592" height="44" rx="8" fill="#dcfce7" stroke="#4ade80"/>
  <text x="76" y="286" font-size="12" font-weight="700" fill="#166534">ARC(Adaptive Replacement Cache) — 读缓存</text>
  <text x="76" y="303" font-size="10" fill="#15803d">MRU(最近最多使用)+ MFU(最近最频繁使用) · 比 page cache 更先进,可设 zfs_arc_max</text>

  <rect x="64" y="318" width="592" height="44" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="76" y="336" font-size="12" font-weight="700" fill="#3730a3">SPA(Storage Pool Allocator) — 物理设备管理层</text>
  <text x="76" y="353" font-size="10" fill="#4f46e5">metaslab→空闲空间管理 · space map→日志式跟踪 · 分配策略 first-fit / best-fit</text>

  <rect x="64" y="368" width="592" height="44" rx="8" fill="#e2e8f0" stroke="#cbd5e1"/>
  <text x="76" y="386" font-size="12" font-weight="700" fill="#334155">VDEV(Virtual Device) — RAID / 设备抽象</text>
  <text x="76" y="403" font-size="10" fill="#475569">disk · mirror · raidz(1/2/3) · special(元数据/dedup) · cache(L2ARC) · log(ZIL/SLOG)</text>
</svg>

---

## COW 事务模型

```
每个写操作:
  1. 分配一个新块 (在目标 metaslab 中找空闲空间)
  2. 写新数据到分配的块
  3. 更新 dnode 的 block pointer (指向新块, 不指向旧块)
  4. dnode 变了 → dnode 本身也要写新位置 → 更新上层指针
  5. 一路往上 → uberblock (root) 更新 → 原子完成

关键: 所有指针更新都是原子的 (单次写 uberblock)
  → 崩溃后要么看到旧状态, 要么看到新状态 — 没有中间态
  → 这就是"没有 journal"的保证 (COW 自身就是一致性的保证)

事务分组 (TXG):
  ZFS 将多个系统调用的写操作合并为一个事务组 (TXG)
  每 ~5 秒提交一次 (zfs_txg_timeout)
  → 减少碎片, 提高写吞吐
  → fsync 绕过这个延迟: 走 ZIL 独立提交
```

---

## SLOG 与 ZIL

```
ZIL (ZFS Intent Log):
  同步写 (O_SYNC / fsync) 的加速器
  正常异步写 → 每 5 秒 TXG → 不需要 ZIL
  同步写 → ZIL 单独记录 → 返回到用户空间 → 快
         → 5 秒后 TXG 提交 → ZIL 记录可丢弃

SLOG (Separate LOG):
  将 ZIL 放在专用设备上 (低延迟 NVMe SSD)
  同步写延迟从 HDD latency 降为 SSD latency
  崩溃恢复: replay ZIL + 最近的 TXG → 完整恢复

ZIL 不是 write cache!
  它只存操作日志 → 数据始终由 TXG 写到主存储
  断电丢失 ZIL → 只丢失最近 5 秒的同步写 (异步写不受影响)
```

---

## 与 Btrfs/bcachefs 的对比

| | ZFS | Btrfs | bcachefs |
|---|---|---|---|
| 许可证 | CDDL | GPLv2 | GPLv2 |
| 内核集成 | DKMS module | in-tree | in-tree (6.7+) |
| COW | 始终 | 元数据始终, 数据可选 | 始终 |
| 快照 | ZVOL, dataset 级别 | subvolume | subvolume |
| RAID | raidz(1/2/3), mirror | raid0/1/10/5/6 | 多设备分层 |
| ARC/L2ARC | 有 | 无 (用 page cache) | 无 |
| 去重 | 在线 (极耗内存) | 离线 (有工具) | 否 |
| 内建压缩 | lz4, zstd, gzip | zstd, lzo, zlib | zstd, lz4, gzip |
| 发送/接收 | zfs send/recv | btrfs send/receive | 规划中 |

ZFS 的明显优势是成熟度 (20+ 年生产部署) 和 ARC 读缓存。Btrfs 的优势是内核主线集成和更轻量的 RAID。bcachefs 太新，稳定性待验证。

---

## 调试

```bash
# ARC 状态 (需要安装 zfs)
cat /proc/spl/kstat/zfs/arcstats

# Pool 状态
zpool status
zpool iostat -v 1

# 当前 TXG
cat /sys/module/zfs/parameters/zfs_txg_timeout
```

---

## 参考与延伸

- **OpenZFS 文档**: https://openzfs.github.io/openzfs-docs/
- **源码**: https://github.com/openzfs/zfs
- **关键论文**: 
  - "The Zettabyte File System" (Sun, 2003)
  - "The Adaptive Replacement Cache" (Megiddo & Modha, 2003)

---

*关键词: ZFS, DMU, ZIL, ARC, SPA, COW, SLOG, dnode, uberblock, TXG, CDDL, DKMS*
