从分区上的子目录启动 Linux 系统?

从分区上的子目录启动 Linux 系统?

我想尝试设置一台计算机,使其在同一文件系统中安装多个 Linux。例如,文件系统有 3 个文件夹:/Ubuntu_Precise/Ubuntu_Oneiric/Ubuntu_Natty

(我知道你可以使用 BTRFS 和子卷来做到这一点,但我想使用 EXT4 来提高速度)。

我曾经使用 BTRFS 设置了不同发行版的多个安装,并且通过使其正常工作,我知道 Grub 可以很好地从“非标准”路径引导 vmlinuz 和 initrd 映像。但是当我在做 BTRFS 的时候,有一个rootflags=subvol=@<subvolume_name>命令告诉内核将该子卷挂载为文件系统中的 / 。是否有任何参数可以传递内核,使其将分区中的子文件夹绑定安装为 / 然后启动?

我认为对于其他部分,我相当接近。我知道如何在/etc/fstab.另外,从我在 BTRFS 子卷中设置多个 linux 安装的系统开始,我就习惯在虚拟机中安装发行版,然后使用 rsync 进行迁移,所以我不太担心需要做什么获得正确的配置,我只是想找出正确的配置是什么。一旦我知道了这一点,我应该能够轻松地迁移到子文件夹和文件编辑。

我已经了解虚拟化和分区,但这不是我想要的。目标计算机没有足够的能力进行虚拟化,并且分区不共享可用空间。我正在寻找建立一个双/三/四/等启动linux发行版的系统,但这是用一个文件系统来实现的,这样就不会出现“我有可用空间,但它位于错误的分区!”的情况。

如果有人有建议如何编辑我的问题或其标题以使其更清晰,我会洗耳恭听。

答案1

简短的回答 - 据我所知,没有现成的工作解决方案可以满足您的具体要求。您必须调整每个发行版的每个 initramfs 来支持您的特定需求。

长答案——是的,这是可能的。现在大多数 Linux 发行版都使用 initramfs,它将由引导加载程序加载到内存中,然后由内核解包。它将在那里运行/sbin/init,负责设置早期的用户空间(运行 udev、加载模块、启动 plymouth、询问加密密码、设置网络以进行网络安装,……凡是你能想到的)。因为您可以运行自己的脚本并评估自定义启动参数。

Debian 示例

如果您使用的是 Debian(应该与 Ubuntu 相同),您应该能够放置一个脚本,/etc/initramfs-tools/scripts/init-bottom/该脚本将在 init 启动之前执行。有关脚本的更多信息,不同的目录和布局请查看man initramfs-tools。您必须调整rootmnt并添加目标目录。

示例(未经测试)脚本应安装为/etc/initramfs-tools/scripts/local-bottom/00-myroot/usr/share/initramfs-tools/scripts/init-top/00-myroot

#!/bin/sh -e

PREREQS=""

prereqs() { echo "$PREREQS"; }

case "$1" in
  prereqs)
  prereqs
  exit 0
;;
esac

for opt in $(cat /proc/cmdline); do
  case $opt in
    rootdir=*)
      new_mntdir="${opt#rootdir=}"
      ;;
    esac
done

if [ -n "$new_mntdir" ] ; then
  echo rootmnt="$rootmnt/$new_mntdir" >> /conf/param.conf
fi

这个想法是调整rootmnt initramfs 脚本中使用的init来启动/执行真正的 init。由于根设备已安装在init-bootom阶段中,因此您只需调整/更改目标目录即可。

要使用此脚本,只需添加一个新的引导参数,复制该脚本,使其可执行,重新生成 initramfs 并为您的 Linux 发行版添加一个引导参数,例如rootdir=/Ubuntu_Precise.

答案2

以下是在 ubuntu focus&bionic(也可能在其他地方)中有效的两种方法。我没有足够的代表来发表评论,但是,bionic:/usr/share/initramfs-tools/init 在调用 mountroot 之后和调用 *-bottom 脚本之前在 /etc/fstab 中查找 /usr,因此添加一个 init-底部脚本(如另一个答案中所建议的)是“太晚了”。相反,我推荐这些:

#!/bin/bash -f
#copyleft 2018 greg mott

#set a subdirectory as root (so multiple installs don't need partitions)
#these work in ubuntu bionic, might need tweaking to work elsewhere
#1st choice:  tweak initramfs-tools/scripts/local
#   pro:  $sub becomes root directly, nothing gets any chance to see the partition root
#   con:  requires the subdirectory's initramfs/initrd to be tweaked and rebuilt
#2nd choice:  specify this scriptfile as init= on the kernel commandline
#   pro:  no need to rebuild initramfs
#   con:  bin/bash in partition root must be executable by $sub/vmlinux (partition root older than $sub likely will work)
#   con:  if the partition root etc/fstab mounts /usr, the $sub initramfs will mount the partition root /usr
#   con:  additional initramfs scripts might also look in the partition root rather than $sub

#for either choice copy /etc/grub.d/40_custom to /etc/grub.d/07_custom and add one or more menuentries that specify subroot:
#menuentry "subroot foo" {
#     echo "subroot foo"
#              sub=/foo
#             uuid=22e7c84a-a416-43e9-ae9d-ee0119fc3894         #use your partition's uuid
#     search --no-floppy --fs-uuid --set=root $uuid
#            linux $sub/vmlinuz ro root=UUID=$uuid subroot=$sub
#     echo "initrd $sub/initrd.img"
#           initrd $sub/initrd.img      #works in recent releases where the /initrd.img softlink is relative
#}

#for the 2nd choice, in addition to subroot= on the kernel commandline also specify:
#   init=/path/to/script        #pathname from partition root to this scriptfile (chmod 744)

#for the 1st choice, the tweak for bionic:/usr/share/initramfs-tools/scripts/local is replace:
#          mount ${roflag} ${FSTYPE:+-t ${FSTYPE} }${ROOTFLAGS} ${ROOT} ${rootmnt}
#          mountroot_status="$?"
#with:
#          set -x
#          karg=" $(cat<proc/cmdline) " m=${karg#* subroot=}
#          [ "$m" = "$karg" ]||subroot=${m%% *}                                  #extract subroot from kernel commandline
#          [ $subroot ]&&part=part||part=$rootmnt                                #no subroot, just mount partition as root
#          mkdir part
#          mount ${roflag} ${FSTYPE:+-t ${FSTYPE} }${ROOTFLAGS} ${ROOT} $part&&  #mount partition
#             if [ "$subroot" ]
#             then mount --bind part/$subroot $rootmnt&&                         #mount subroot
#                  umount part                       #&&sleep 15                 #unmount partition root (uncomment &&sleep for time to watch)
#             fi
#          mountroot_status="$?"
#          [ $mountroot_status = 0 ]||sleep 90                                   #if error pause to see it
#          set +x
#once you've edited /usr/share/initramfs-tools/scripts/local, update-initramfs -u will rebuild for the current kernel,
#and it will automatically build into every new initrd/initramfs installed thereafter

subroot(){ karg=" $(cat<proc/cmdline) " m=${karg#* subroot=}
           [ "$m" = "$karg" ]||subroot=${m%% *}                 #extract subroot from kernel commandline
           [ $subroot ]||return 0                               #no subroot, just proceed in partition root
           while read -r m r m
           do for m in $M x                                     #build list of what's already mounted
              do    [[ $r = $m* ]]&&break                       #exclude subtrees (eg dev/**)
              done||[[ $r = /   ]]||M=$M\ $r                    #exclude /
           done<proc/mounts
           (set -x;mount --bind $subroot mnt)||{ set -x         #mount subroot
                                                 sleep 30          #if not found pause to see error
                                                 return 0;}        #then reincarnate as partition root init
           for m in $M
           do (set -x;mount -n --move $m mnt$m)||return         #move listed mounts to subroot
           done
           set -x
           cd           mnt&&
           pivot_root . mnt&&                                   #subroot becomes root
           umount -l    mnt&&                                   #unmount partition root
          #sleep 15        &&                                   #so far so good?  uncomment for time to look
           exec chroot . init "$@"                              #reincarnate as subroot init
}
subroot "$@"&&exec init "$@"||exec bash                         #land in a shell if moves or pivot fail

答案3

出于不同的目的,引导不同的 Linux 而不弄乱分区表是很有趣的,共享文件系统的另一种解决方案是使用循环卷,这里需要进行一些更改,假设您有一个 /debian 循环文件/卷到 /dev/sdb1 文件系统中(我为主操作系统和循环操作系统使用当前的 GNU/Debian sid/unstable)。

/etc/grub.d/40_custom: # outside from loop volume
menuentry 'label' --class gnu-linux --class gnu --class os {
    ...
    loopback loop (hd2,msdos1)/debian
    linux   (loop)/boot/vmlinuz root=/dev/sdb1 loop=/debian ro
    initrd  (loop)/boot/initrd
}

作为 Linux 命令行定义到 grub 中的参数由 initrd /init 设置为 env,因此:

ROOT=/dev/sdb1
rootmnt=/root
loop=/debian 

循环允许将卷挂载到“自身”上,默认脚本流程执行mount /dev/sdb1 /root我们只需选择将 /dev/sdb1 重新挂载为 rw(如果它是 ro),然后始终附加一个mount -o loop /root/debian /root.

/etc/initramfs-tools/scripts/local-bottom/loop: # inside the loop volume
#!/bin/sh

[ "$1" = "prereqs" ] && echo && exit 0

if [ -n "${loop}" ]; then
        if [ "${readonly}" = "y" ]; then
                roflag=-r
                mount -o remount,rw ${ROOT} ${rootmnt}
        else
                roflag=-w
        fi
        mount ${roflag} -o loop ${rootmnt}${loop} ${rootmnt}
fi

还需要将一些模块预加载到 initram 中(然后不要忘记运行 update-initramfs)

/etc/initramfs-tools/modules: # inside the loop volume
...
loop
ext4

不知道使用循环对性能或浪费资源有多大影响,我想知道在 ext4 上安装 ext4 是否会使文件系统失败的概率增加一倍,但猜测可以进行一些调整。也许有更好的方法来使用循环,不那么hackish,如果有请告诉我,因为我还没有找到。

答案4

这不是一个答案,但我想澄清有关乌尔里希的答案和评论的一些观点(我无法在上面发表评论)。

乌尔里希提出的解决方案“可能”有效(尚未测试),但随后你会得到一个不可重装文件系统。作为解决方法(恕我直言,丑陋),您可以在 chroot 之前将 fs 安装为 rw (正如这里所建议的)但要小心损坏的初始化脚本。我想这个解决方法有更多的副作用(比如损坏的 fs 试图重新安装 ro 并且失败)。

我正在使用带有 ext4 的内核 3.2,并在 chroot 内安装已安装的开发,但仍给出 EBUSY,如 psusi 评论的那样。

相关内容