如何在没有 udev hook 的情况下使用最小的 initramfs 启动到根 btrfs 文件系统?

如何在没有 udev hook 的情况下使用最小的 initramfs 启动到根 btrfs 文件系统?

Arch Linux 是 UEFI 从具有 GPT 和三个分区的 USB 闪存驱动器启动:

  1. EFI(vfat 文件系统)
  2. root(btrfs 文件系统,由 ext4 转换而来)
  3. home(btrfs 文件系统,由 ext4 转换而来)

btrfs 分区没有子卷,并且位于单个磁盘(USB 闪存驱动器)上。这里没有使用LVM。

任务

试图创建一个最小initramfs通过删除 udev 和许多其他钩子。使用 mkinitcpio 优化启动也被用作灵感。
有效的mkinitcpio钩子是:根据,自动侦测修改配置文件

Btrfs 钩子

btrfs 钩子未启用,因为mkinitcpio钩子文档btrfs 挂钩列表:

在单个设备上使用 Btrfs 不需要此挂钩。

回归

  1. 我尝试删除 udev -> 启动错误
  2. 我尝试添加 btrfs 模块 -> 启动错误
  3. 我添加了 btrfs 钩子 -> 启动错误
  4. 将 root=PARTUUID= 更改为 root=UUID= 符号 -> 启动错误
  5. 添加参数 rootfstype=btrfs -> 启动错误
  6. rootdelay=0 -> 启动错误
  7. rootdelay=10 -> 启动错误
  8. 从紧急 shell 使用 /dev/sda2 挂载 -> ok

错误

只有插入 udev 或 systemd hooks 后系统才会 root 到 btrfs 根分区,否则会出现此错误:

ERROR: device 'PARTUUID=c2...c13' not found. Skipping fsck.
:: mounting 'PARTUUID=c2...c13' on real root 
mount: can't find 'PARTUUID=c2...c13'
You are now being dropped into an emergency shell.

运行时初始化调试/日志输出

启用启动参数rd.debugrd.log显示“premount”调用解析设备函数并返回一个空查找。

resolve_device PARTUUID=c2...c13
local major minor dev tag device=PARTUUID=c2...c13
blkid -lt PARTUUID=c2...c13 -o device
dev=

最后一个空的开发导致设备未找到错误。

initramfs挂载命令

mount_handler=default_mount_handler
...
# Mount root at /new_root
"$mount_handler" /new_root

来源:https://git.archlinux.org/mkinitcpio.git/tree/init

default_mount_handler() {
    msg ":: mounting '$root' on real root"
    mount ${rootfstype:+-t $rootfstype} -o ${rwopt:-ro}${rootflags:+,$rootflags} "$root" "$1"

来源:https://git.archlinux.org/mkinitcpio.git/tree/init_functions#n375

initramfs挂载版本

[rootfs ]# mount -V
mount from util-linux 2.29.2 (libmount 2.29.2: btrfs, assert, debug)

initramfs 内容

$ lsinitcpio -a /boot/initramfs-linux-tiny.img
==> Image: /boot/initramfs-linux-tiny.img
==> Created with mkinitcpio 23
==> Kernel: 4.10.3-1-ARCH
==> Size: 3.53 MiB
==> Compressed with: lz4 -l
  -> Uncompressed size: 8.32 MiB (.424 ratio)
  -> Estimated extraction time: 0.028s

==> Included modules:
  ahci [explicit]         hid-generic [explicit]      raid6_pq            usbcore
  atkbd [explicit]        i8042 [explicit]        scsi_mod            usbhid [explicit]
  btrfs [explicit]        libahci             sd_mod [explicit]       xhci-hcd
  crc32c-intel [explicit]     libata              serio               xhci-pci [explicit]
  crc32c_generic          libcrc32c           serio_raw [explicit]        xor
  ehci-hcd            libps2              uas [explicit]
  ehci-pci [explicit]         ohci-hcd            usb-common
  hid                 ohci-pci [explicit]         usb-storage

==> Included binaries:
  blkid       busybox     dosfsck     fsck        fsck.vfat   kmod        mount       switch_root

紧急 shell 的blkid命令列出了正确的(部分)UUID 值。使用 (PART)UUID 进行安装是否会因为没有而失败/dev/disk/

问题

在没有 udev 的情况下,启动到位于 USB 闪存驱动器上的非 raid 非子卷单驱动器根 btrfs 分区需要什么?


initramfs/initPS 此错误可能是由 RACE 条件引起的,执行命令时 UUID/PARTUUID 尚不可用mount ... UUID=...

答案1

原因

在版本 23 中,mkinitcpioresolve_device() 函数仅被调用一次。当执行时驱动器标签尚未读取时,blkid无法查找/dev/...请求标签的内核驱动器 ( ) 名称。

解决方案

通过添加“without-udev”挂钩(如下所示),resolve_device 函数保持不变。尽管标准可用 mkinitcpio 功能可以覆盖 mount_handler 添加 run_hook用于轮询直到blkid返回一个值,或者(超时)10 秒已经过去。因此,可以从 mkinitcpio 配置中删除“udev”挂钩。

笔记

  • 该解决方案是在以下人员的帮助下创建的法尔科宁迪
  • 在早期启动阶段出现涉及 fsck 的错误消息。为了删除该消息,without-udev 挂钩已被重写为使用 arun_hook而不是mount_handler.新的代码甚至更短。

$ cat /usr/lib/initcpio/hooks/without-udev
#!/bin/ash
# Minimal initramfs files are created without udev.
# This hooks provides a polling disk mount replacement for udev.
# Udev hook can be removed, resulting in smaller initramfs files.

run_hook () {
    local dev timeout sleepval device=$root
    # if udev is running then exit
    [ "$udevd_running" -eq 1 ] && return
    # try for (timeout * sleepval =) 10 seconds to handle slow (USB) devices
    timeout=1000
    sleepval=0.01

    case $device in
        # label to resolve, when resolved the kernel block device also exists
        UUID=*|LABEL=*|PARTUUID=*|PARTLABEL=*)
            while [ $timeout -gt 0 ]; do
                timeout=$((timeout - 1))
                dev=$(blkid -lt "$device" -o device)
                [ -n "$dev" ] && timeout=0 || sleep $sleepval
            done
            ;;
        # kernel named block device, poll for existence
        /dev/*)
            while [ $timeout -gt 0 ]; do
                timeout=$((timeout -1))
                if [ -b "$device" ]; then
                    dev=$device
                    timeout=0
                else
                    sleep $sleepval
                fi
            done
            ;;
    esac
}

# vim:set syntax=sh:

$ cat /usr/lib/initcpio/install/without-udev
#!/bin/bash

build() {
    add_runscript
}

help() {
    cat <<HELPEOF
This hook provides support for booting without the "udev" hook,
including support for UUID, LABEL, PARTUUID, PARTLABEL.
HELPEOF
}

# vim: set ft=sh ts=4 sw=4 et:

相关内容