缘起
我之前在电脑上装过双系统或者多系统,但对于多个系统的启动项一直云里雾里,经常出现启动项丢失、启动项重复等问题。这次我打算好好研究一下多系统启动项的配置,包括启动加载器的设置、EFI/Grub/rEFInd等,以及如何定制多系统启动器主题。
操作系统启动流程
计算机的启动过程主要分为4步:1. 上电;2. BIOS/UEFI启动;3. 加载启动项;4. 操作系统启动。第1步和第4步一般不需要用户干预,而第2步和第3步需要用户配置。所以这里主要介绍第2步和第3步的内容。
BIOS/UEFI启动
BIOS(Basic Input/Output System)和UEFI(Unified Extensible Firmware Interface)是计算机的固件,负责启动计算机并加载操作系统。BIOS是旧的启动方式,UEFI是新的启动方式,UEFI相对于BIOS有更多的功能和更好的性能。无论是UEFI还是BIOS,它们都是主板上的固件。
BIOS启动流程
BIOS一般在比较旧的计算机上使用,其启动流程比较固定,用户定制的自由度比较低。主要分为以下步骤:
-
BIOS POST(Power-On Self-Test)自检
-
BIOS读取MBR(Master Boot Record)中的引导程序
-
引导程序读取分区表,找到活动分区
-
引导程序读取活动分区的引导扇区,加载操作系统
MBR的位置是固定的,一般在硬盘的第一个扇区,大小为512字节。MBR中包含了分区表和引导程序,分区表记录了硬盘的分区信息,引导程序负责加载操作系统。
UEFI启动流程
UEFI也是BIOS的一种,一般在比较新的计算机上使用。其启动流程比较灵活,包含的功能更多,用户定制的自由度比较高。尽管UEFI的功能更多,但其流程更为统一,主要分为以下步骤:
-
UEFI POST(Power-On Self-Test)自检
-
UEFI读取EFI分区中的引导程序
-
引导程序读取EFI分区中的引导文件,加载操作系统
相比BIOS,UEFI使用的EFI分区的位置并不固定,甚至一块硬盘上可以有多个EFI分区。
UEFI的引导程序和引导文件都存储在EFI分区中,包含了引导程序、引导文件、驱动程序等。只要引导文件符合UEFI的规范,无论是Windows、Linux还是macOS的引导程序,也无论引导文件放在哪个EFI分区,UEFI都可以找到并加载。
由于UEFI更灵活也更新,所以接下来讲的多系统启动项配置都是基于UEFI的。
概念辨析:启动加载器、启动项、启动文件
在讲多系统启动项配置之前,我们先来梳理一下启动加载器、启动项和启动文件的概念。
-
启动加载器(Boot Loader):负责加载操作系统的启动引导程序,每个启动引导程序就是一个启动项(Boot Entry)。常用的启动加载器有Grub(Grub2)、rEFInd、Clover等。
-
启动项(Boot Entry):启动加载器加载的每个操作系统就是一个启动项,每个启动项对应一个操作系统。启动项包含了操作系统的启动文件、内核、驱动程序等。
-
启动文件(Boot File):启动项中的启动文件,是操作系统的引导程序,负责加载操作系统。Windows的启动文件是
bootmgfw.efi
,Linux的启动文件是vmlinuz
。
在此基础上,我们再来看UEFI是如何启动操作系统的。
-
每个启动加载器都对应着一个
.efi
文件,这个.efi
文件就是启动加载器的启动文件,负责加载启动加载器。在UEFI完成自检后,会读取EFI分区中的所有.efi
文件,如果你在此时进入BIOS设置,看到的启动项就是EFI分区中的.efi
文件。这些启动项是有优先顺序的,其顺序可以在BIOS设置中调整。例如下图中最高优先级的启动项是
rEFInd
,其次是Windows Boot Manager
,最后是ubuntu
。电脑启动时会先试图加载rEFInd
,如果rEFInd
不存在,再尝试加载Windows Boot Manager
,以此类推。 -
如果
rEFInd
成功加载,rEFInd
会读取EFI分区中的refind.conf
配置文件,根据配置文件生成启动菜单,显示在屏幕上。其中每个菜单项对应一个启动项,启动项一般有两类:一类是操作系统内核镜像,另一类是启动加载器。- 操作系统内核镜像:对应的是操作系统的内核文件,例如Linux的
vmlinuz
文件,Windows的bootmgfw.efi
文件。 - 启动加载器:对应的是启动加载器的
.efi
文件,例如Grub2的grubx64.efi
文件。
- 操作系统内核镜像:对应的是操作系统的内核文件,例如Linux的
-
用户选择一个菜单项后,
rEFInd
会加载对应的启动项,如果启动项是操作系统内核镜像,rEFInd
会直接加载操作系统;如果启动项是启动加载器,rEFInd
会加载启动加载器,启动加载器再加载操作系统。 -
如果启动加载器要启动一个Linux系统,那么首先会加载Linux的内核文件
vmlinuz-xxx
,然后加载Linux的初始化文件系统文件initrd-xxx.img
或者initramfs-xxx.img
,最后加载Linux系统。这两个文件都在Linux系统的/boot
目录下。
多系统安装时的启动项配置
上面说了UEFI的引导程序和引导文件都存储在EFI分区中,所以在安装多个系统时,每个系统都会在EFI分区中创建一个引导文件,这样就会有多个引导文件,每个引导文件对应一个系统。这样就可以通过选择不同的引导文件来启动不同的系统。
在安装多个操作系统时,我们可以把各个系统的引导文件放入同一个EFI分区中,也可以把各个系统的引导文件放入不同的EFI分区中。两种方法各有利弊,具体选择哪种方法取决于个人喜好。
-
同一个EFI分区
这种情况下的硬盘分区结构如下:
优点:方便管理,只需要一个EFI分区,不需要多个EFI分区。更易于配置启动加载器。
缺点:所有的引导文件都在一个EFI分区中,万一EFI分区损坏,所有的系统都无法启动。
安装多个系统的具体操作如下:
- 硬盘分区:在硬盘上分出一个EFI分区,大小最好为1GB以上,格式为FAT32。其他分区可以按照各个操作系统的需求而定,例如Windows一般需要至少52GB的空间,Linux一般需要至少20GB的空间。
- 安装Windows:如果安装的多个操作系统既有Windows又有Linux,建议先安装Windows。因为Windows的引导程序会覆盖其他已安装操作系统的引导程序,导致先前安装的Linux无法启动。安装Windows时,选择给Windows分配的分区,Windows会自动在EFI分区中创建引导文件。
- 依次安装各个Linux:对于每个Linux系统,安装时一般选择自定义分区,手动挂载EFI分区到
/boot/efi
,挂载为Linux准备的分区到/
,可选择创建交换分区和/home
分区。安装完成后,Linux会在EFI分区中创建引导文件。 - 配置启动加载器:多个操作系统依次安装完成后,我们需要配置启动加载器,让我们可以选择要启动的操作系统。这些内容会在后面详细介绍。
-
不同的EFI分区
这种情况下的硬盘分区结构如下:
优点:每个系统有自己的EFI分区,互不干扰。一个系统的EFI分区损坏,其他系统不受影响。
缺点:需要多个EFI分区,管理起来比较麻烦。每个系统的引导文件都在不同的EFI分区中,不易于配置启动加载器。
安装多个系统的具体操作如下:
- 硬盘分区:在硬盘为每个系统分别分出一块空间。暂时不需要创建EFI分区,安装系统时会在每个系统各自的分区中分别创建EFI分区。
- 安装Windows:安装Windows时,选择给Windows分配的分区,Windows会自动在该分区中创建EFI分区。
- 安装Linux:对于每个Linux系统,安装时选择为这个系统分配的分区,在这个分区中创建大小为200MB左右的EFI分区,挂载点为
/boot/efi
;剩余的空间挂载为Linux准备的分区,挂载点为/
;可选择创建交换分区和/home
分区。 - 配置启动加载器:多个操作系统依次安装完成后,我们需要配置启动加载器,让我们可以选择要启动的操作系统。这些内容会在后面详细介绍。
启动目录结构
我们来看一下启动目录的结构,以及安装一个操作系统时,这个操作系统会创建哪些文件。这里假设多个操作系统的引导文件都放在同一个EFI分区中,我们安装了3个操作系统:Windows、Ubuntu、Arch Linux。其中Ubuntu系统使用Grub2作为启动加载器,Arch Linux系统使用systemd-boot作为启动加载器。我们还在Ubuntu系统下安装了rEFInd作为启动加载器。
-
EFI分区
EFI分区是一个FAT32格式的独立的分区,一般大小为1GB左右。EFI分区中主要包含各个启动加载器的引导文件。
EFI分区的目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
EFI ├── Boot │ └── bootx64.efi ├── Microsoft │ └── Boot │ ├── BCD │ ├── BCD.LOG │ ├── BCD.LOG1 │ ├── BCD.LOG2 │ ├── bootmgfw.efi │ ├── bootmgr.efi │ └── boot.stl ├── systemd │ └── systemd-bootx64.efi └── ubuntu ├── grub.cfg ├── grubx64.efi ├── mmx64.efi └── shimx64.efi
EFI/Boot/bootx64.efi
:UEFI的默认引导文件,一般指向Windows的引导文件EFI/Microsoft/Boot/bootmgfw.efi
。EFI/Microsoft/Boot/bootmgfw.efi
:Windows的引导文件,负责加载Windows操作系统。EFI/systemd/systemd-bootx64.efi
:systemd-boot的引导文件,负责加载Linux操作系统。EFI/ubuntu/grubx64.efi
:Grub2的引导文件,负责加载Linux操作系统。EFI/ubuntu/shimx64.efi
:Secure Boot的引导文件,用于加载Grub2。EFI/ubuntu/mmx64.efi
:Grub2的引导文件,用于加载Grub2。EFI/ubuntu/grub.cfg
:Grub2的配置文件,包含了各个启动项的配置。
-
安装操作系统
安装Ubuntu时,选择自定义分区,手动挂载EFI分区到
/boot/efi
,挂载为Linux准备的分区到/
。安装完成后,Ubuntu会在EFI分区中创建EFI/ubuntu
目录,其中包含了Grub2的引导文件和配置文件。Ubuntu会在/boot
目录中创建两个文件:vmlinuz
和initrd.img
,这两个文件是Linux的内核文件和初始化文件系统文件。Grub2的引导文件会识别这两个文件,并创建启动项,在启动时选择相应的启动项,就可以加载Ubuntu系统。在Ubuntu系统中,可以进入
/boot/efi/EFI
中查看EFI分区的目录结构。1 2 3 4 5 6 7 8 9
/boot ├── efi │ └── EFI │ ├── Boot │ ├── Microsoft │ ├── systemd │ └── ubuntu ├── vmlinuz └── initrd.img
注意,虽然
/boot/efi/EFI
在/boot
下,但/boot/efi/EFI
中的文件在EFI分区里,但/boot
下的其他文件在Ubuntu系统的分区里。/boot
下的vmlinuz
和initrd.img
文件在其他系统中是无法访问的,而若其他系统也挂载了EFI分区,那么其他系统也可以访问/boot/efi/EFI
中的文件。如果继续安装更多的操作系统,一般操作系统都会让你选择启动加载器,你可以选择使用Grub2,也可以选择systemd-boot,或者其他启动加载器。下面介绍一下启动加载器的配置。
启动加载器
启动加载器(Boot Loader)负责加载操作系统的启动引导程序,每个启动引导程序就是一个启动项(Boot Entry)。常用的启动加载器有Grub(Grub2)、rEFInd、Clover等。这里主要介绍Grub2和rEFInd。
Grub2
Grub2(GNU GRand Unified Bootloader 2)是Linux系统上常用的启动加载器,功能强大,支持多系统启动。Grub2的配置文件是/boot/grub/grub.cfg
,一般由grub-mkconfig
命令生成。
Grub2的配置文件比较复杂,但功能强大,可以定制各种启动项。Grub2的主题也可以定制,但相对比较复杂。
Grub2的配置
-
配置文件
Grub2的主配置文件是
/boot/grub/grub.cfg
,可以通过编辑这个文件来配置Grub2的启动项。1
sudo vim /boot/grub/grub.cfg
不过一般不建议直接编辑
grub.cfg
文件,因为这个文件是由grub-mkconfig
命令生成的,如果直接编辑grub.cfg
文件,下次更新Grub2时会被覆盖。建议通过配置/etc/default/grub
文件,以及/etc/grub.d/
目录下的配置文件来配置Grub2。 -
启动项的配置
Grub2的启动项配置比较复杂,例如:
1 2 3 4 5
GRUB_DEFAULT=0 GRUB_TIMEOUT=5 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" GRUB_CMDLINE_LINUX=""
GRUB_DEFAULT
:默认启动项的编号,从0开始。GRUB_TIMEOUT
:启动菜单显示的时间,单位为秒。GRUB_DISTRIBUTOR
:系统的发行商。GRUB_CMDLINE_LINUX_DEFAULT
:Linux内核的默认启动参数。GRUB_CMDLINE_LINUX
:Linux内核的启动参数。
-
定制主题
Grub2的主题可以定制,可以把想要的主题文件放在
/boot/grub/themes
目录下。主题目录下包含了主题文件、字体文件、背景图片等,而且有一个名为theme.txt
的配置文件,可以配置主题的各种属性。在配置文件
/etc/default/grub
中可以指定主题文件:1
GRUB_THEME="/boot/grub/themes/theme-name/theme.txt"
更多的Grub2配置可以参考Grub2配置。
rEFInd
rEFInd是一个基于UEFI的启动加载器,功能强大,支持多系统启动。rEFInd的配置文件是/boot/efi/EFI/refind/refind.conf
,一般由refind-install
命令生成。
rEFInd的配置文件比较简单,但功能强大,可以定制各种启动项。rEFInd的主题也可以定制,相对比较简单。
rEFInd安装
rEFInd的安装比较简单,可以直接下载rEFInd的安装包,解压到EFI分区中。也可以使用refind-install
命令安装rEFInd。
|
|
安装完成后,rEFInd会在EFI分区中创建EFI/refind
目录,其中包含了rEFInd的引导文件和配置文件。
rEFInd配置
rEFInd的配置文件是/boot/efi/EFI/refind/refind.conf
,可以通过编辑这个文件来配置rEFInd的启动项。
|
|
-
启动项的配置
rEFInd中启动项的配置比较简单,例如:
1 2 3
timeout 5 hideui singleuser scanfor manual,external
timeout
:启动菜单显示的时间,单位为秒。hideui
:隐藏启动菜单,只显示单用户模式。scanfor
:扫描启动项的方式,可以扫描手动配置的启动项,也可以扫描外部设备的启动项。
-
定制主题
rEFInd的主题可以定制,可以把想要的主题文件放在
/boot/efi/EFI/refind/theme
目录下。主题文件是一个CSS文件,可以修改颜色、字体、背景等。在配置文件
/boot/efi/EFI/refind/refind.conf
中可以指定主题文件:1
include themes/theme-name/theme.conf
-
屏蔽启动项
有时候rEFInd会扫描到很多不需要的启动项,例如如果你在安装系统时选择了使用Grub2作为启动加载器(Deepin, Fedora等系统会默认安装Grub2,而且似乎没法在安装时取消),那么rEFInd会扫描到Grub2的启动项。你可以通过配置文件
/boot/efi/EFI/refind/refind.conf
来屏蔽这些启动项。rEFInd可以选择忽略某些文件(dont_scan_files
)、工具(dont_scan_tools
)、目录(dont_scan_dirs
)和磁盘(dont_scan_volumes
)。1 2
dont_scan_files shim.efi,mmx64.efi dont_scan_dirs ESP:/EFI/ubuntu,EFI/deepin,EFI/fedora
问题解决
-
在SD卡或U盘上安装系统
- 如果你的电脑有SD卡插槽,你可以选择把系统安装在SD卡上。
- 跟上面讲的安装多个操作系统的方法相似,在SD卡上安装操作系统也有两种EFI分区方式,一种是在SD卡上创建一个EFI分区,另一种是在安装系统时把硬盘上的EFI分区挂载到
/boot/efi
。 - 对于
/boot
分区,可以选择直接在SD卡上创建,也可以先在硬盘上创建一个大小约为500M的ext4分区,然后在安装系统时把它挂载到/boot
。/boot
里主要存放内核文件和引导文件,通常200M左右的空间就够了,但是如果你选择备份多个内核,那么就需要更大的空间。Fedora系统会限制/boot
分区不小于512M。 - 如果你选择把
/boot
分区放在SD卡上,你可能会遇到一些安全方面的限制,例如Secure Boot可能会阻止从SD卡启动。你需要注册注册密钥,或者关闭Secure Boot。 - 我一般选择把EFI分区和
/boot
分区都放在硬盘上。
我还没试过在U盘上安装系统,不过原理应该和在SD卡上安装是一样的。
-
Fedora无法启动
在SD卡上安装了Fedora系统后,我遇到了系统无法启动的问题。rEFInd可以扫描到Fedora的内核文件
vmlinuz-xxx
和initramfs-xxx.img
,但是启动到一半会卡住,报错:1 2 3 4 5 6 7
Failed to switch root: Specified switch root path /sysroot does not seem to be an OS tree. os-release file is missing. initrd-switch-root.service: Main process exited, code=exited, status=1/FAILURE initrd-switch-root.service: Failed with result 'exit-code'. Failed to start initrd-switch-root.service: Switch Root. Startup finished in 1.073s (kernel) + 1.000s (initrd) + 1.000s (userspace) = 3.073s. initrd-switch-root.service: Triggering OnFailure= dependencies. Started emergency.service - Emergency Shell.
在Emergency Shell中查看后发现,
/sysroot
目录为空,没有任何文件。而正常情况下,加载内核和文件系统初始化后,/sysroot
目录应该包含操作系统的文件。所以这个问题是因为在启动内核和初始化文件系统时,SD卡没有被正确挂载。你可以在Emergency Shell中手动挂载SD卡,然后
exit
退出Emergency Shell,系统就可以正常启动了。1 2
mount /dev/sda1 /sysroot exit
但显然这不是一个好的解决方案,因为每次启动都要手动挂载SD卡。所以正确的解决方案是让rEFInd在启动内核时正确挂载SD卡,这需要给启动选项添加一个
root
参数,指定根目录。你可以在rEFInd的配置文件/boot/efi/EFI/refind/refind.conf
中添加一个带有options
参数的菜单项。1 2 3 4 5 6
menuentry "Fedora" { volume "SD-boot" loader /vmlinuz-xxx initrd /initramfs-xxx.img options "root=/dev/sda1" }
然而,这个解决方案也不是很好,因为
- 在每次更新内核后,你都需要手动修改rEFInd的配置文件。
- 如果你有多个内核,这个菜单项不能自动生成子菜单,你需要手动添加每个内核的菜单项。
所以,更好的解决方案是让整个
boot
分区里的内核文件和引导文件都使用整个选项。做法是在/boot
分区的根目录下创建一个refind_linux.conf
文件,然后在rEFInd的配置文件/boot/efi/EFI/refind/refind.conf
中添加一个root
参数。1
"Boot with standard options" "root=/dev/sda1"
问题终于解决了!