标签
# 为 Debian、Fedora 和 Ubuntu 创建持久化 Live USB 镜像 Live USB 是快速启动 Linux 环境的绝佳方式,但默认情况下,每次重启后所有更改都会丢失。**持久化存储**功能可以将你的文件、配置和已安装的软件包保留下来。遗憾的是,各发行版对这一功能的实现方式并不统一——每个发行版都有自己的一套做法,内核参数、分区标签和文件系统结构各不相同。 本文将介绍如何通过注入 ext4 分区并利用 OverlayFS 的持久化机制,为 Debian、Fedora 和 Ubuntu 创建持久化 Live USB。此外还会介绍一个不需要重新构建镜像、直接修改 ISO 启动参数的"野路子"字节替换技巧。 --- ## 背景:Live 系统如何实现持久化 大多数现代 Linux Live 系统使用 **OverlayFS** 将只读的压缩根文件系统(通常是 SquashFS)与可写的上层目录叠加在一起。启动时,内核或 initramfs 会在只读层之上挂载一个可写层,使系统看起来像一个完整的可读写文件系统。 持久化功能的原理是:将这个上层目录(overlay upperdir)存放到 USB 驱动器上一个**可写的持久化分区**中,而不是放在内存中。 **挑战在于**:每个发行版寻找持久化分区的方式都不一样: - **Ubuntu/Debian**:使用内核参数 `persistent`,并查找标签为 `casper-rw` 或 `persistence` 的分区 - **Fedora**:使用内核参数 `rd.live.overlay`,并查找标签为 `overlay` 的分区 - **各发行版**的 initramfs 脚本、overlay 目录结构和文件系统要求也各有差异 --- ## 工具准备 ```bash # 安装所需工具 sudo apt install gdisk e2fsprogs util-linux # Debian/Ubuntu sudo dnf install gdisk e2fsprogs util-linux # Fedora ``` 你还需要: - 一个容量足够的 USB 驱动器(建议 16GB 以上) - 目标发行版的 ISO 镜像 - `root` 权限 - (可选)用于字节替换技巧的十六进制编辑器 --- ## 第一步:将 ISO 写入 USB 首先,按照常规方式将 ISO 写入 USB 驱动器: ```bash # 找到你的 USB 设备 lsblk # 写入 ISO(请将 /dev/sdX 替换为实际的设备名) sudo dd if=ubuntu-24.04-desktop-amd64.iso of=/dev/sdX bs=4M status=progress oflag=sync ``` **注意**:`dd` 写入后,分区表反映的是 ISO 内部的布局。ISO 镜像通常不会占满整个 USB 驱动器,末尾会留有大量未使用的空间——我们将利用这部分空间创建持久化分区。 --- ## 第二步:添加持久化分区 ISO 写入后,USB 末尾应有未分配的空间。我们将在这里创建一个新的 ext4 分区。 ### 查看当前分区布局 ```bash sudo gdisk -l /dev/sdX ``` 记下最后一个分区的结束扇区,新分区将从下一个扇区开始。 ### 创建新分区 ```bash sudo gdisk /dev/sdX ``` 在 `gdisk` 交互界面中: ``` Command: n # 新建分区 Partition number: (接受默认值) First sector: (接受默认值,即紧接上一分区之后) Last sector: (接受默认值,使用全部剩余空间;或指定大小,如 +8G) Hex code: 8300 # Linux 文件系统 Command: w # 写入并退出 ``` ### 格式化分区 这是关键步骤——**分区标签必须与发行版的 initramfs 脚本所期望的完全一致**。 #### Ubuntu(casper) ```bash sudo mkfs.ext4 -L casper-rw /dev/sdX3 ``` Ubuntu 的 `casper` initramfs 脚本会扫描所有已挂载的设备,查找标签为 `casper-rw` 的分区。 #### Ubuntu(较新版本使用 persistence) 某些 Ubuntu 版本和 Ubuntu 衍生版使用不同的标签: ```bash sudo mkfs.ext4 -L persistence /dev/sdX3 ``` 使用 `persistence` 标签时,还需要在分区根目录创建一个配置文件: ```bash sudo mount /dev/sdX3 /mnt echo "/ union" | sudo tee /mnt/persistence.conf sudo umount /mnt ``` #### Debian(live-boot) ```bash sudo mkfs.ext4 -L persistence /dev/sdX3 # 挂载并创建配置文件 sudo mount /dev/sdX3 /mnt echo "/ union" | sudo tee /mnt/persistence.conf sudo umount /mnt ``` Debian 的 `live-boot` 包需要 `persistence.conf` 文件来指定哪些目录应该被持久化。`/ union` 表示使用 union 挂载方式持久化整个根目录。 #### Fedora(dracut) ```bash sudo mkfs.ext4 -L overlay /dev/sdX3 ``` Fedora 使用 `dracut` 而非 `casper`,其持久化机制完全不同。`dracut` 的 `dmsquash-live` 模块会查找标签为 `overlay` 的分区。 --- ## 第三步:修改内核启动参数 创建好分区后,还需要在启动参数中告诉内核(或 initramfs)启用持久化功能。有三种方式可以实现。 ### 方式一:在启动菜单中手动编辑(适合测试) 启动时,在 GRUB 或 syslinux 菜单处按 `e`(GRUB)或 `Tab`(syslinux),然后手动添加参数: **Ubuntu/Debian:** ``` persistent ``` **Fedora:** ``` rd.live.overlay=LABEL=overlay rd.live.overlay.overlayfs=1 ``` ### 方式二:修改 USB 上的配置文件(推荐) 如果 USB 上的 EFI 或 syslinux 分区可写,可以直接修改配置文件。 挂载 EFI 分区: ```bash sudo mount /dev/sdX2 /mnt ``` 找到启动配置文件: ```bash find /mnt -name "grub.cfg" -o -name "isolinux.cfg" -o -name "*.conf" | head -20 ``` **对于 Ubuntu 的 grub.cfg:** ```bash sudo nano /mnt/boot/grub/grub.cfg ``` 找到 `linux` 行,添加 `persistent`: ``` linux /casper/vmlinuz boot=casper persistent quiet splash --- ``` **对于 Fedora 的 grub.cfg:** ``` linux /images/pxeboot/vmlinuz rd.live.image rd.live.overlay=LABEL=overlay rd.live.overlay.overlayfs=1 quiet ``` ### 方式三:字节替换技巧(无需重新构建 ISO) 这是最"野路子"的方法,但在 USB 上的文件系统为只读时(如某些混合 ISO 镜像)非常有用。其原理是:直接在 ISO/USB 设备的原始字节层面,将现有的内核参数替换为新的参数。 **前提条件**:替换后的字符串长度必须与原字符串**完全相同**,不足的部分用空格或空字节填充。 #### 查找参数字符串 ```bash # 在设备中搜索目标字符串 sudo grep -c "boot=casper" /dev/sdX # 确认字符串存在 sudo grep -oba "boot=casper" /dev/sdX | head -5 ``` `grep -oba` 会输出字符串的字节偏移量: ``` 12345678:boot=casper ``` #### 使用 Python 执行字节替换 ```python #!/usr/bin/env python3 """ ISO/USB 内核参数字节替换工具 警告:直接修改块设备——操作前务必备份! """ import sys device = "/dev/sdX" # 原始字符串(长度必须与替换字符串一致) old_param = b"boot=casper" # 新字符串(添加 persistent 参数) new_param = b"boot=casper persistent" # 注意:两者长度不同,需要调整 # 更实用的示例:在现有参数中添加 'persistent' # 找一个可以安全覆盖的较长字符串 old_string = b"boot=casper quiet splash" new_string = b"boot=casper persistent " # 用空格填充,保持长度相同 assert len(old_string) == len(new_string), "字符串长度必须相同!" print(f"原始字符串:{old_string}") print(f"替换字符串:{new_string}") print(f"长度:{len(old_string)} 字节") with open(device, "r+b") as f: data = f.read() count = data.count(old_string) print(f"找到 {count} 处匹配") if count == 0: print("未找到目标字符串,请检查参数") sys.exit(1) # 替换所有匹配项 new_data = data.replace(old_string, new_string) f.seek(0) f.write(new_data) print("替换完成!") ``` > ⚠️ **警告**:直接写入块设备极具风险。操作前务必确认设备名称,并做好备份。 #### 更安全的替换方式(按偏移量操作) ```python #!/usr/bin/env python3 """按偏移量进行精确字节替换""" import subprocess import sys device = sys.argv[1] if len(sys.argv) > 1 else "/dev/sdX" old_bytes = b"boot=casper quiet" new_bytes = b"boot=casper persi" # 长度相同 # 使用 grep 查找偏移量 result = subprocess.run( ["grep", "-oba", old_bytes.decode(), device], capture_output=True, text=True ) offsets = [] for line in result.stdout.strip().split('\n'): if ':' in line: offset = int(line.split(':')[0]) offsets.append(offset) print(f"在以下偏移量找到匹配:{offsets}") with open(device, "r+b") as f: for offset in offsets: f.seek(offset) found = f.read(len(old_bytes)) print(f"偏移量 {offset} 处:{found}") # 确认后再写入 confirm = input("是否替换?(y/N) ") if confirm.lower() == 'y': f.seek(offset) f.write(new_bytes) print(f"已替换偏移量 {offset} 处的内容") print("操作完成") ``` --- ## 各发行版详细说明 ### Ubuntu(casper) Ubuntu 使用 `casper` 包处理 Live 系统启动。持久化功能的相关代码位于 `/usr/share/initramfs-tools/scripts/casper` 及其模块目录中。 **工作原理:** 1. casper 在启动时扫描所有块设备 2. 查找标签为 `casper-rw` 的分区,或包含 `persistence.conf` 的 `persistence` 分区 3. 使用 OverlayFS 将其挂载为上层目录 4. 将合并后的文件系统作为根目录挂载 **完整配置示例:** ```bash # 1. 写入 ISO sudo dd if=ubuntu-24.04-desktop-amd64.iso of=/dev/sdX bs=4M status=progress # 2. 创建持久化分区 sudo gdisk /dev/sdX # n -> 默认 -> 默认 -> 默认 -> 8300 -> w # 3. 格式化(使用 casper-rw 标签) sudo mkfs.ext4 -L casper-rw /dev/sdX3 # 4. 修改启动参数(如果 EFI 分区可写) sudo mount /dev/sdX2 /mnt/efi sudo sed -i 's/boot=casper/boot=casper persistent/g' /mnt/efi/boot/grub/grub.cfg sudo umount /mnt/efi ``` ### Debian(live-boot) Debian 的 Live 系统使用 `live-boot` 包,其配置方式与 Ubuntu 的 casper 类似,但有一些重要区别。 **工作原理:** 1. live-boot 查找标签为 `persistence` 的分区 2. 读取分区根目录的 `persistence.conf` 文件 3. 根据配置文件的指令创建 OverlayFS **`persistence.conf` 格式:** ``` # 持久化整个根目录(最常用) / union # 或者只持久化特定目录 /home union /etc union ``` **完整配置示例:** ```bash # 1. 写入 ISO sudo dd if=debian-12.0.0-amd64-live-gnome.iso of=/dev/sdX bs=4M status=progress # 2. 创建并格式化持久化分区 sudo gdisk /dev/sdX # 新建分区 sudo mkfs.ext4 -L persistence /dev/sdX3 # 3. 创建 persistence.conf sudo mount /dev/sdX3 /mnt echo "/ union" | sudo tee /mnt/persistence.conf sudo umount /mnt # 4. 修改启动参数(添加 persistence 参数) # 在 GRUB 菜单中手动添加,或修改配置文件 ``` **Debian 需要的内核参数:** ``` persistence persistence-label=persistence ``` 或仅使用: ``` persistence ``` (`live-boot` 会自动查找 `persistence` 标签的分区) ### Fedora(dracut + dmsquash-live) Fedora 的 Live 系统机制与 Debian/Ubuntu 完全不同,使用 `dracut` 和 `dmsquash-live` 模块。 **工作原理:** 1. dracut 的 `dmsquash-live` 模块处理 SquashFS 根文件系统的挂载 2. 通过 `rd.live.overlay` 参数指定 overlay 分区 3. 使用 device mapper 而非直接使用 OverlayFS(取决于版本) **Fedora 持久化分区设置:** ```bash # 1. 写入 ISO sudo dd if=Fedora-Workstation-Live-x86_64-39.iso of=/dev/sdX bs=4M status=progress # 2. 创建持久化分区 sudo gdisk /dev/sdX # 新建分区 sudo mkfs.ext4 -L overlay /dev/sdX4 # 注意:Fedora 有更多默认分区 # 3. 修改 GRUB 参数(添加以下内容) # rd.live.overlay=LABEL=overlay rd.live.overlay.overlayfs=1 ``` **Fedora 所需的内核参数:** ``` rd.live.image rd.live.overlay=LABEL=overlay rd.live.overlay.overlayfs=1 ``` 如需重置 overlay(丢弃所有持久化更改),可添加: ``` rd.live.overlay.reset ``` --- ## 故障排查 ### 持久化功能不生效 **检查分区标签:** ```bash sudo blkid /dev/sdX3 # 应显示:LABEL="casper-rw" 或 LABEL="persistence" 或 LABEL="overlay" ``` **检查内核参数:** ```bash cat /proc/cmdline # 应包含 persistent(Ubuntu/Debian)或 rd.live.overlay(Fedora) ``` **检查 Live 系统日志:** ```bash # Ubuntu/Debian dmesg | grep -i "casper\|persist\|overlay" journalctl -b | grep -i "live\|persist" # Fedora dmesg | grep -i "overlay\|live" ``` ### USB 重启后无法识别 部分 BIOS/UEFI 对 GPT 磁盘(而非混合 MBR)的兼容性存在问题。可尝试以下方法: ```bash # 混合 MBR 写入(如有必要) sudo gdisk /dev/sdX # 进入 expert 菜单:x # 创建混合 MBR:h # 按提示操作,选择前 3 个分区 ``` ### Fedora overlay 分区未找到 Fedora 的 dracut 有时在识别 overlay 分区时存在时序问题。可尝试添加以下参数: ``` rd.live.overlay=LABEL=overlay rootflags=noatime ``` 或直接使用设备路径代替标签: ``` rd.live.overlay=/dev/sdb4 ``` ### 分区空间已满 ```bash # 查看持久化分区使用情况(在 Live 系统中执行) df -h /run/live/persistence/sdb3 # Debian/Ubuntu df -h /run/overlayfs # Fedora # 清理 apt 缓存 sudo apt clean sudo apt autoremove ``` --- ## 局限性与注意事项 ### 内核更新 内核更新是持久化 Live 系统的一大痛点。由于内核文件存储在只读的 ISO 部分,常规的 `apt upgrade` 或 `dnf upgrade` 无法更新内核。可以安装内核,但新内核的 GRUB 条目不会生效,因为 GRUB 配置同样位于只读区域。 **解决方法**:手动将新内核文件复制到可写的 EFI 分区,并更新 GRUB 配置——但这需要额外的手动操作,本文不作详述。 ### 性能 持久化 overlay 层会带来一定的性能开销,因为每次文件系统操作都需要经过 OverlayFS 层。对于写入密集型任务,建议使用高速 USB 3.0 驱动器。 ### 跨发行版兼容性差 如前所述,各发行版之间缺乏标准化: | 发行版 | initramfs | 参数 | 分区标签 | 配置文件 | |--------|-----------|------|----------|----------| | Ubuntu(旧版)| casper | `persistent` | `casper-rw` | 不需要 | | Ubuntu(新版)| casper | `persistent` | `persistence` | 需要 | | Debian | live-boot | `persistence` | `persistence` | 需要 | | Fedora | dracut | `rd.live.overlay` | `overlay` | 不需要 | 这意味着:同一个 USB 驱动器无法同时为多个发行版提供持久化支持(至少不能轻松实现)。 ### 字节替换技巧的局限性 字节替换方法虽然便捷,但存在一些限制: - 新旧字符串长度必须相同 - 如果参数被压缩或哈希校验,替换会失败 - 某些 GRUB 版本会对配置文件进行校验,替换后可能无法启动 - UEFI Secure Boot 环境下可能遇到额外的校验问题 --- ## 总结 为不同的 Linux 发行版创建持久化 Live USB 是完全可行的,但因为缺乏统一标准,每个发行版都需要单独对待: 1. **Ubuntu** 使用 casper,需要 `casper-rw` 或 `persistence` 标签,以及 `persistent` 内核参数 2. **Debian** 使用 live-boot,需要 `persistence` 标签、`persistence.conf` 文件,以及 `persistence` 内核参数 3. **Fedora** 使用 dracut,需要 `overlay` 标签,以及 `rd.live.overlay` 内核参数 字节替换技巧为修改只读 ISO 的启动参数提供了一种无需重新构建镜像的快捷方式,但使用时需格外谨慎。 希望 Linux 社区未来能够在 Live 系统持久化功能上实现更好的标准化——但就目前而言,了解这些底层细节对于让持久化 Live USB 正常工作仍然是必要的。