ZFS文件系统

Table of Contents

ZFS 简介

将 ZFS 称为文件系统有点名不副实,因为它在传统意义上不仅仅是个文件系统。ZFS 将 逻辑卷管理器 的概念与功能丰富的和 可大规模扩展 的文件系统结合起来。首先探索一些 ZFS 所基于的原则:

  • ZFS 使用 池存储 模型,而不是传统的基于卷的模型: ZFS 视存储为可根据需要动态分配(和缩减)的共享池。这优于传统模型,在传统模型中,文件系统位于卷上,使用独立卷管理器来管理这些资产
  • ZFS 内嵌入的是重要功能集
    • 快照
    • 即写即拷,克隆
    • 连续完整性检查
    • 通过 RAID-Z 的数据保护
  • 可以在 ZFS 卷的顶端使用其他的文件系统(如 ext4):这意味着可以获得那些 ZFS 的功能,如独立文件系统中的快照(该文件系统可能并不直接支持它们)

ZFS 不只是组成有用文件系统的功能集合。相反,它是 构建出色文件系统 的集成和 补充功能 的集合

存储池

ZFS 合并了 卷管理 功能来提取 底层物理存储设备 到文件系统:

  • ZFS 对存储池(zpools)进行操作,而不是直接查看物理块设备,存储池构建自 虚拟驱动器 ,可由驱动器或驱动器的一部分物理地进行表示
  • 可以动态构造这些池,甚至这些池正在活跃地使用时也可以

即写即拷

ZFS 使用 即写即拷 模型来管理存储中的数据。虽然这意味着数据永远不会写入到位(从来没有被覆盖),而是写入新块并更新元数据来引用数据。即写即拷有利的原因有多个,不仅仅是因为它可以启用的快照和克隆等一些功能:

  • 由于从来不覆盖数据,这可以更简单地确保存储 永远不会处于不一致 的状态:因为在新的写入操作完成以后较早的数据仍保留
  • 这允许 ZFS 基于 事务 ,且更容易实现类似原子操作等的功能
  • 文件系统的所有写入都成为 顺序写入 (因为始终进行重新映射):避免存储中的热点并利用顺序写入的性能(比随机写入更快)

数据保护

可以使用 ZFS 的众多保护方案之一来保护由虚拟设备组成的存储池:

  • 跨两个或多个设备 (RAID 1)来对池进行镜像
  • 通过 奇偶校验 来保护该池(类似于 RAID 5)
  • 跨动态带区宽度 来镜像池

基于池中设备数量,ZFS 支持各种不同的的奇偶校验方案:

  • 通过 RAID-Z (RAID-Z 1) 来保护三个设备
  • 对于四个设备,可以使用 RAID-Z 2(双重奇偶校验,类似于 RAID6)
  • 对于更大的保护来说,可以将 RAID-Z 3 用于更大数量的磁盘进行三重奇偶校验
  • 为提高速度(不存在错误检测以外的数据保护),可以跨设备进行条带化(RAID 0)
  • 可以创建条带化镜像(来镜像条带化设备),类似于 RAID 10
ZFS 的一个有趣属性随 RAID-Z、即写即拷事务和动态条带宽度的组合而来

在传统的 RAID 5 体系结构中,所有磁盘都必须在条带内具有其自己的数据,或者条带不一致
因为没有方法自动更新所有磁盘,所以这可能产生众所周知的 RAID 5 写入漏洞问题(其中在 RAID 集的驱动器中条带是不一致的)
假设 ZFS 处理事务且从不需要写入到位,则写入漏洞问题就消除了

此方法的另外一个便捷性体现在磁盘出现故障且需要重建时
传统的 RAID 5 系统使用来自该集中其他磁盘的数据来重建新驱动器的数据
RAID-Z 遍历可用的元数据以便只读取有关几何学的数据并避免读取磁盘上未使用的空间
随着磁盘变得更大以及重建次数的增加,此行为变得更加重要

校验和

虽然数据保护提供了在故障时重新生成数据的能力,但是这并不涉及处于第一位的数据的有效性。ZFS 通过为写入的每个块的元数据生成 32 位校验和 (或 256 位散列)解决了此问题:

  • 在读取块时,将 验证此校验和 以避免静默数据损坏问题
  • 在有数据保护(镜像或 AID-Z)的卷中,可自动读取或 重新生成备用数据

在 ZFS 上校验和与元数据存储在一起,因此如果提供数据保护(RAID-Z) ,可以检测并更正错位写入

快照和克隆

由于 ZFS 的即写即拷性质,类似快照和克隆的功能变得易于提供。因为 ZFS 从不覆盖数据而是写入到新的位置,所以可以保护较早的数据(但是在不重要的情况下被标记为删除以逆转磁盘空间):

  • 快照:旧块的保存以便及时维护给定实例中的文件系统状态。这种方法也是空间有效的,因为无需复制(除非重新写入文件系统中的所有数据)
    • 克隆:一种快照形式,在其中获取可写入的快照。在这种情况下,由每一个克隆共享初始的未写入块,且被写入的块仅可用于特定文件系统克隆

可变块大小

传统的文件系统由匹配后端存储(512 字节)的静态大小的块组成。ZFS 为各种不同的使用实现了 可变块大小 (通常大小达到 128KB,但是您可以变更此值):

  • 可变块大小的一个重要使用是 压缩 (因为压缩时的结果块大小理想情况下将小于初始大小)
    • 除了提供更好的存储网络利用外,此功能也使存储系统中的 浪费最小化 (因为传输更好的数据到存储需要更少的时间)
  • 支持可变块大小还意味着您可以针对所期望的特定工作量优化块大小,以便改进性能

其他

ZFS 并入了许多其他功能,如 重复数据删除 (最小化数据重复)、 可配置的复制加密缓存管理的自适应更换缓存 以及 在线磁盘清理 (标识并修复在不使用保护时可以修复的潜在错误)。它通过巨大的可扩展性来实现该功能,支持 16 千兆兆个字节的可寻址存储(264 字节)

入门

单个磁盘存储池

在单个磁盘上创建一个简单, 非冗余的 ZFS, 使用 zpool 命令:

$ zpool create example /dev/da0

可以通过 df 的输出查看新的存储池:

$ df

Filesystem  1K-blocks    Used    Avail Capacity  Mounted on
/dev/ad0s1a   2026030  235230  1628718    13%    /
devfs               1       1        0   100%    /dev
/dev/ad0s1d  54098308 1032846 48737598     2%    /usr
example      17547136       0 17547136     0%    /example

这份输出清楚的表明了 example 存储池不仅创建成功而且被挂载了。 能像访问普通的文件系统那样访问它, 就像以下例子中演示的那样,用户能够在上面创建文件并浏览:

$ cd /example
$ ls

$ touch testfile
$ ls -al
total 4
drwxr-xr-x   2 root  wheel    3 Aug 29 23:15 .
drwxr-xr-x  21 root  wheel  512 Aug 29 23:12 ..
-rw-r--r--   1 root  wheel    0 Aug 29 23:15 testfile

遗憾的是这个存储池并没有利用到 ZFS 的任何特性。 在这个存储池上创建一个文件系统,并启用压缩:

$ zfs create example/compressed
$ zfs set compression=gzip example/compressed

现在 example/compressed 是一个启用了压缩的 ZFS 文件系统了。 可以尝试复制一些大的文件到 /example/compressed

使用这个命令可以禁用压缩:

$ zfs set compression=off example/compressed

使用如下的命令卸载这个文件系统,并用 df 工具确认:

$ zfs umount example/compressed
$ df

Filesystem  1K-blocks    Used    Avail Capacity  Mounted on
/dev/ad0s1a   2026030  235232  1628716    13%    /
devfs               1       1        0   100%    /dev
/dev/ad0s1d  54098308 1032864 48737580     2%    /usr
example      17547008       0 17547008     0%    /example

重新挂在这个文件系统使之能被访问, 并用 df 确认:

$ zfs mount example/compressed
$ df

Filesystem         1K-blocks    Used    Avail Capacity  Mounted on
/dev/ad0s1a          2026030  235234  1628714    13%    /
devfs                      1       1        0   100%    /dev
/dev/ad0s1d         54098308 1032864 48737580     2%    /usr
example             17547008       0 17547008     0%    /example
example/compressed  17547008       0 17547008     0%    /example/compressed

存储池与文件系统也可通过 mount 的输出查看:

$ mount

/dev/ad0s1a on / (ufs, local)
devfs on /dev (devfs, local)
/dev/ad0s1d on /usr (ufs, local, soft-updates)
example on /example (zfs, local)
example/data on /example/data (zfs, local)
example/compressed on /example/compressed (zfs, local)

正如前面所提到的,ZFS 文件系统, 在创建之后就能像普通的文件系统那样使用。然而, 还有很多其他的特性是可用的。在下面的例子中, 将创建一个新的文件系统,data。 并要在上面存储些重要的文件, 所以文件系统需要被设置成把每一个数据块都保存两份拷贝:

$ zfs create example/data
$ zfs set copies=2 example/data

现在可以再次使用 df 查看数据和空间的使用状况:

$ df

Filesystem         1K-blocks    Used    Avail Capacity  Mounted on
/dev/ad0s1a          2026030  235234  1628714    13%    /
devfs                      1       1        0   100%    /dev
/dev/ad0s1d         54098308 1032864 48737580     2%    /usr
example             17547008       0 17547008     0%    /example
example/compressed  17547008       0 17547008     0%    /example/compressed
example/data        17547008       0 17547008     0%    /example/data

注意:存储池上的 每一个文件系统都有着相同数量的可用空间

这就是在这些例子中使用 df 的原因, 是为了文件系统都是从相同的存储池取得它们所需的空间

ZFS 去掉了诸如卷和分区此类的概念, 并允许多个文件系统占用同一个存储池

不再需要文件系统与存储池的时候能像这样销毁它们:

$ zfs destroy example/compressed
$ zfs destroy example/data
$ zpool destroy example

RAID-Z

磁盘无法避免的会坏掉和停止运转。 当这块磁盘坏掉的时候,上面的数据都将丢失。 一个避免因磁盘损坏而丢失数据的方法是使用 RAID。ZFS 在它的存储池设计中支持这样的特性。假设存在 3 个 SCSI 设备, da0, da1 和 da2 。 使用如下的命令创建一个 RAID-Z 存储池:

$ zpool create storage raidz da0 da1 da2

数据校验

ZFS 使用 校验和 (checksum) 来检查存储数据的完整性。 这时在文件系统创建时 自动启用 的,可使用以下的命令禁用:

$ zfs set checksum=off storage/home

这不是个明智的选择,因为校验和 不仅非常有用而且只需占用少量的存储空间。 并且启用它们也不会明显的消耗过多资源。 启用后就可以让 ZFS 使用校验和校验来检查数据的完整。 这个过程通常称为 scrubbing 。 可以使用以下的命令检查 storage 存储池里数据的完整性:

$ zpool scrub storage

这个过程需花费相当长的时间,取决于存储的数据量。 而且 I/O 非常密集, 所以在任何时间只能执行一个这样的操作。 在 scrub 完成之后,状态就会被更新, 可使用如下的命令查看:

$ zpool status storage

pool: storage
state: ONLINE
scrub: scrub completed with 0 errors on Sat Aug 30 19:57:37 2008
config:

NAME        STATE     READ WRITE CKSUM
storage     ONLINE       0     0     0
raidz1    ONLINE       0     0     0
da0     ONLINE       0     0     0
da1     ONLINE       0     0     0
da2     ONLINE       0     0     0

errors: No known data errors

这个例子中完成时间非常的清楚。 这个特性可以帮助你在很长的一段时间内确保数据的完整

管理

ZFS 管理由两个工具组成:

  • zpool: 控制存储池和增加、删除、替换和管理磁盘
  • zfs: 增加、删除和管理文件系统和卷

zpool

创建和销毁存储池

创建一个ZFS寸尺池包含许多预先的设定,因为存储池的结构在创建之后是没法改变的。 最重要的决定是什么类型的虚拟磁盘(vdev)来构成物理磁盘。一旦存储池构建完毕后,绝大多数类型的虚拟磁盘不允许添加额外的物理磁盘,例外是镜像(mirrors)和条带(stripe: 可以从镜像升级)。尽管额外的虚拟磁盘可以被添加到存储池,但是存储池的布局在创建存储池之后仍然不会被改变。因此,在销毁和重建存储池之前必须备份

创建一个简单的镜像池:

$ zpool create mypool mirror /dev/ada1 /dev/ada2
$ zpool status

pool: mypool
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada1    ONLINE       0     0     0
            ada2    ONLINE       0     0     0

errors: No known data errors

多个虚拟磁盘可以被同时创建,之间用 虚拟磁盘类型 来分割:

$ zpool create mypool mirror /dev/ada1 /dev/ada2 mirror /dev/ada3 /dev/ada4
  pool: mypool
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada1    ONLINE       0     0     0
            ada2    ONLINE       0     0     0
          mirror-1  ONLINE       0     0     0
            ada3    ONLINE       0     0     0
            ada4    ONLINE       0     0     0

errors: No known data errors

存储池可以使用分区来构建,而不是使用整块硬盘,使用分区来创建 RAID-Z2 存储池:

$ zpool create mypool raidz2 /dev/ada0p3 /dev/ada1p3 /dev/ada2p3 /dev/ada3p3 /dev/ada4p3 /dev/ada5p3
$ zpool status

  pool: mypool
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0
            ada2p3  ONLINE       0     0     0
            ada3p3  ONLINE       0     0     0
            ada4p3  ONLINE       0     0     0
            ada5p3  ONLINE       0     0     0

errors: No known data errors

存储池可以被销毁来回收空间

zpool

实例

利用 ZFS 文件系统的通用功能 (即利用面向使用了基于 fstab 方法的 OS 数据集的挂载点功能) 结合 GRUB ,实现一个双引导 OS

概述

ZFS 系统同 FreeBSD 和 Linux 上“典型的”ZFS设置有些微不同。某些数据集 (比如 home 目录) 是由两个操作系统共享的, 但是位于各自挂载点中的 OS 数据集则要依赖于所使用的OS,而因此 ZFS 特定的挂载点功能是不能有效使用的

假定存储卷的名字是 data

整个方案看起来如下:

  • data/home 被挂载到 /home, 与其所有的子数据集一起使用 ZFS 挂载点系统
  • data/freebsd 及其子数据集包含了 FreeBSD 系统,而其所有的挂载点都被设置成 legacy
  • data/gentoo 及其子数据集包含了 Gentoo 系统,其所有的挂载点也都被设置成 legacy

OS 和 GRUB 概述

两个 OS 都必须利用 /etc/fstab 方法来挂载他们大多数的文件系统, 因此不能使用 ZFS 挂载点功能。这样每个 OS 就都需要一个不同的 fstab

data/home 数据集 (以及其它类似的数据集) 都会使用 ZFS 挂载点方法来进行挂载,而不是 fstab

两个OS都要通过一个特殊的顶层目录 (Gentoo 上是 /freebsd, FreeBSD 上是 /gentoo) 来 访问另外一个系统的数据

GRUB 引导加载程序可以被用来提供一个 引导选择设施 ,而无需进行大的修改和配置 (不过要知道输入到 grub.cfg 文件中去的类型的神奇方式!)

设置过程

设置过程包括如下几个步骤:

  1. 使用 FreeBSD 安装器创建 GPT 和 ZFS 池
  2. 使用 FreeBSD 启动加载程序安装并配置 FreeBSD
  3. 启动并进入 FreeBSD 创建 Gentoo Linux 数据集,安装 GRUB
  4. 启动并进入 Gentoo Linux 安装器,安装 Gentoo
  5. 启动并进入 Gentoo, 完成配置任务
当然也可以将步骤倒着进行,先安装 Gentoo,然后再使用它的工具

只是上列顺序能使 FreeBSD 上 GPT 的创建和 GRUB 的安装顺利许多

准备工作

执行这个安装过程,需要为两个OS安装 memstick 镜像,还需要有途径能进行互联网访问

FreeBSD 安装程序

引导进入 FreeBSD 安装程序,通过引导程序菜单选择 手动分区 模式。这样做会进入一个 shell 操作环境,创建分区并将所有的东西挂载到 /mnt

创建分区和 ZFS 池

使用 gpart 工具来 创建分区

gpart create -s gpt ada0
gpart create -s gpt ada1
gpart add -t efi -l efi-system -s 200M ada0
gpart add -t freebsd-zfs -l zfs-data ada0
gpart add -t linux-swap -l swap -s 96G ada0
gpart add -t freebsd-zfs -l zfs-data-log -s 16G ada0
gpart add -t freebsd-zfs -l zfs-data-cache ada0

用新的分区来 创建一个 ZFS 池 ,并使用 DOS 文件系统 来对 EFI 系统分区 进行 格式化

newfs_msdos /dev/ada0p1

zpool create -m legacy -o atime=off -o checksum=sha256 data /dev/ada0p2 log /dev/ada0p2 cache /dev/ada0p3
注意:已经将 atime (它可以大大降低磁盘的写入量)关掉了,并且将校验算法设置成了 sha256

ada1 磁盘是一块装进去的 SSD(固态硬盘):

  • 如果没有 SSD,那么做日志或者缓存方面的设置就没啥意义
  • 16GB 的意向日志是有点多余,但是它可以减轻设备的压力
  • 根数据集 的挂载点设置成了 legacy

创建 ZFS 数据集

有了一个 ZFS 存储池后,接下来要做的就是 创建数据集 。先创建 FreeBSD 根目录 (root)并对其进行挂载

zfs create -o compression=lz4 data/freebsd

mount -t zfs data/freebsd /mnt/
注意:它会从父目录继承 “legacy”挂载点

创建一些挂载点目录:

mkdir /mnt/home
mkdir /mnt/gentoo/
mkdir /mnt/tmp
mkdir /mnt/usr
mkdir /mnt/var

下面是一个相当精细的 ZFS 方案, 它对不同的目录设置不同的可执行性、权限和压缩比等属性。这样可以达到很显著的压缩比,有效的增加了磁盘的可用空间:

zfs create -o exec=on -o setuid=off -o compression=off data/freebsd/tmp
zfs create -o exec=on -o setuid=on -o compression=lz4 data/freebsd/usr
zfs create -o exec=off -o setuid=off -o compression=gzip data/freebsd/usr/include
zfs create -o exec=on -o setuid=off -o compression=lz4 data/freebsd/usr/lib
zfs create -o exec=on -o setuid=off -o compression=lz4 data/freebsd/usr/lib32
zfs create -o exec=on -o setuid=off -o compression=gzip data/freebsd/usr/libdata
zfs create -o exec=on -o setuid=on -o compression=lz4 data/freebsd/usr/local
zfs create -o exec=on -o setuid=off -o compression=gzip data/freebsd/usr/local/etc
zfs create -o exec=off -o setuid=off -o compression=gzip data/freebsd/usr/local/include
zfs create -o exec=on -o setuid=off -o compression=lz4 data/freebsd/usr/local/lib
zfs create -o exec=on -o setuid=off -o compression=lz4 data/freebsd/usr/local/lib32
zfs create -o exec=on -o setuid=off -o compression=gzip data=freebsd/usr/local/libdata
zfs create -o exec=on -o setuid=off -o compression=gzip data/freebsd/usr/local/share
zfs create -o exec=off -o setuid=off -o compression=off data/freebsd/usr/local/share/info
zfs create -o exec=off -o setuid=off -o compression=off data/freebsd/usr/local/share/man
zfs create -o exec=on setuid=on -o compression=lz4 data/freebsd/obj
zfs create -o exec=on -o setuid=on -o compression=lz4 data/freebsd/usr/ports
zfs create -o exec=off -o setuid=off -o compression=lz4 data/freebsd/usr/ports
zfs create -o exec=on -o setuid=off -o compression=gzip data/freebsd/usr/share
zfs create -o exec=off -o setuid=off -o compression=off data/freebsd/usr/share/info
zfs create -o exec=off -o setuid=off -o compression=off data/freebsd/usr/share/man
zfs create -o exec=off -o setuid=off -o compression=gzip data/freebsd/usr/src
zfs create -o exec=off -o setuid=off -o compression=lz4 data/freebsd/var
zfs create -o exec=off -o setuid=off -o compression=off data/freebsd/var/db
zfs create -o exec=off -o setuid=off -o compression=lz4 data/freebsd/var/db/pkg
zfs create -o exec=off -o setuid=off -o compression=gzip data/freebsd/var/log
zfs create -o exec=off -o setuid=off -o compression=off data/freebsd/var/empty
zfs create -o exec=off -o setuid=off -o compression=gzip data/freebsd/var/mail
zfs create -o exec=on -o setuid=off -o compression=off data/freebsd/var/tmp
因为 FreeBSD 对于某些文件的所在位置有着非常严格要求,所以这个方案效果很好

对于 Gentoo,由于 Linux 更倾向于宽松的文件定位,可以采取一个更简单的方案:

zfs create -o exec=on -o setuid=off -o compression=off data/gentoo/tmp
zfs create -o exec=on -o setuid=on -o compression=lz4 data/gentoo/usr
zfs create -o exec=off -o setuid=off -o compression=lz4 data/gentoo/var

最后的任务是手动挂载所有的文件系统,命令模版如下:

mount -t zfs data/freebsd/<path> /mnt/<path>

安装配置 FreeBSD

现在输入“exit”能返回到 FreeBSD 安装程序,并且所有东西都挂载在 mnt 目录。剩余部分的安装工作很简单,但是需要到 shell 界面来完成最后两个配置任务。

在 shell 界面执行下面的命令, 然后进入新安装的 FreeBSD 系统:

chroot /mnt

创建 fstab

由于挂载到每一个系统的 ZFS 数据集合是在不同的路径下, 因此需要为它们创建一个 /etc/fstab 文件. 下面这个 fstab 会将数据集合挂载到正确的位置:

data/freebsd/tmp /tmp zfs rw 0 0
data/freebsd/usr /usr zfs rw 0 0
data/freebsd/usr/include /usr/include zfs rw 0 0
...
data/gentoo/ /gentoo zfs rw 0 0
data/gentoo/tmp /gentoo/tmp zfs rw 0 0
...
proc /proc procfs rw 0 0

安装 FreeBSD 原生引导程序

安装 FreeBSD 引导加载程序来执行首次进入系统的操作。执行如下几个步骤进行安装:

mount -t msdosfs /dev/ada0p1 /mnt
mkdir /mnt/efi
mkdir /mnt/efi/BOOT
cp /boot/boot1.efi /mnt/efi/BOOT/BOOTX64.EFI

然后设置 zpool 上的 bootfs 参数 , 以便 FreeBSD 引导加载程序可以选到正确的数据集:

zpool set -o bootfs=data/freebsd data

可能还需要为某些硬件设置 EFI 系统分区上的 bootme 标识

gpart set -a bootme -i 1 ada0

现在你的系统可以直接进入 OS 了

FreeBSD 主要操作系统

现在可以直接引导进入 FreeBSD 了

安装 GRUB

grub-efi port 会安装一个兼容 EFI 系统的 GRUB 版本。这个port要比在 Gentoo 上进行同等的操作简单得多。安装方法如下:

cd /usr/ports/sysutils/grub2-efi
make install clean

需要使用 grub-mkconfig 命令 创建一个 grub.cfg 文件 ,过程中可能需要对该命令进行编辑。也可以直接使用下面这个文件 (确保文件在/boot/grub/grub.cfg):

insmod part_gpt
insmod zfs

menuentry 'FreeBSD' --class freebsd --class bsd --class os {
  search.fs_label data ZFS_PART
  echo "Loading FreeBSD Kernel..."
  kfreebsd ($ZFS_PART)/freebsd/@/boot/kernel/kernel
  kfreebsd_loadenv ($ZFS_PART)/freebsd/@/boot/device.hints
  kfreebsd_module_elf ($ZFS_PART)/freebsd/@/boot/kernel/opensolaris.ko
  kfreebsd_module_elf ($ZFS_PART)/freebsd/@/boot/kernel/acl_nfs4.ko
  kfreebsd_module_elf ($ZFS_PART)/freebsd/@/boot/kernel/zfs.ko
  set kFreeBSD.vfs.root.mountfrom=zfs:data/freebsd
  set kFreeBSD.vfs.root.mountfrom.options=rw}

menuentry 'Gentoo Linux' {
  search.fs_label data ZFS_PART
  linux ($ZFS_PART)/gentoo@/boot/kernel dozfs=force root=ZFS=data/gentoo
  initrd ($ZFS_PART)/gentoo@/boot/initramfs
}

注意:此时已为 Gentoo 创建了一项配置。最后, 需要挂载 EFI 系统分区 然后 安装 GRUB :

mount -t msdosfs /dev/ada0p1 /mnt
grub-install --efi-directory=/mnt --disk-module=efi

这样做会将 GRUB 引导程序安装到 EFI 系统分区的 /efi/grub/grub.efi,仍然需要将它拷贝到相应位置。在此之前,建议先对 FreeBSD 本地的引导加载程序进行备份:

cp /mnt/efi/BOOT/BOOTX64.EFI /mnt/efi/BOOT/BOOTX64.BAK

这样能省去在操作有误时的回退操作。现在拷贝 GRUB 引导加载程序:

cp /mnt/efi/grub/grub.efi /mnt/efi/BOOT/BOOTX64.EFI

之后需要对 GRUB 引导加载程序进行测试,以确保它能有效的对系统进行重新启动,并引导进入 FreeBSD

Gentoo 安装程序

下一个任务是安装 Gentoo 基础系统。 Gentoo 的安装可以通过命令行手动完成

注意,因为使用了 ZFS 作为根文件系统,因此需要使用 genkernel 来安装内核

挂载文件系统

要使用 FreeBSD, 就会需要对文件系统进行挂载:

zpool import -f data
mount -t zfs data/gentoo /mnt/gentoo
mkdir /mnt/gentoo/tmp
mkdir /mnt/gentoo/usr
mkdir /mnt/gentoo/var
mount -t zfs data/gentoo/tmp /mnt/gentoo/tmp
mount -t zfs data/gentoo/usr /mnt/gentoo/usr
mount -t zfs data/gentoo/var /mnt/gentoo/var

现在可以按照 Gentoo 的安装步骤来进行

创建 fstab

使用 FreeBSD 系统时, 需要创建一个 /etc/fstab 文件,它与 FreeBSD 的版本类似, 不过 gentoo 文件系统被挂载到了根目录的相对位置下,而 FreeBSD 文件系统则被挂载到了 /freebsd 目录的相对位置下:

data/freebsd/tmp /freebsd/tmp zfs rw 0 0
data/freebsd/usr /freebsd/usr zfs rw 0 0
data/freebsd/usr/include /freebsd/usr/include zfs rw 0 0
...
data/gentoo/tmp /tmp zfs rw 0 0
data/gentoo/usr /usr zfs rw 0 0
...

构建内核、ZFS 模块以及 initramfs

需要设置一个支持 ZFS 的内核。你可以在这里查看指南(跳转到 “配置内核(Configuring the Kernel)” 一节查阅)

已经将 GRUB 的安装放在 /boot/kernel 和 /boot/initramfs 之后。 Gentoo 会默认将其内核安装到 /boot/kernel-<版本信息>, initramfs 也是如此。然后,需要使用 /boot/kernel 和 /boot/initramfs 这两个名称来创建符号链接,或者自行将文件拷贝到正确的位置

最后是 Gentoo 的安装

在重启之前,请务必阅读该小节。下列检查列出了一些必要操作,如有遗漏则需重新进入安装程序进行操作:

  • 设置一个根密码,这样才可以真正登录进去
  • 安装可以使用无线网络的软件
  • 不要漏掉 /etc/fstab 中的存储卷(如果遗漏了 /var, 系统间的互操作会很繁琐)

启动进入 Gentoo

直接从 GRUB 启动进入 Gentoo,并成功在单 ZFS 文件系统里实现了双引导。但在创建用户主目录之前,需要为 /home 创建一个 ZFS 数据集。在 Gentoo 系统中,进行如下操作:

rm /home || rmdir /home
rm /freebsd/home || rmdir /freebsd/home
mkdir /home
mkdir /freebsd/home
zfs create -o mountpoint=/home -o exec=on -o setuid=off -o compression=lz4 data/home

总结

这样的设置相当强大且灵活,是内核高手及驱动开发者的福音。下面列出了是进行该设置的好处:

  • 可以从 FreeBSD 系统访问一个完整的 Linux 系统,包括内核的源代码
  • 便于实验性质的系统间切换操作
  • 在某个系统坏掉时可以有效地恢复机制