背景

我有2台笔记本电脑,一台旧的,一台相对新一些;多块硬盘,分别安装了我不同时期使用的操作系统。

  • 旧的:BIOS,用1块100GB机械硬盘安装了双系统: Debian 和 Manjaro;
  • 新的:UEFI,有2 块硬盘,其中750GB机械硬盘安装了 Manjaro 系统,200GB固态硬盘安装了Win10 系统;

我最常用的系统是 Manjaro(安装在新电脑的750G硬盘上的那个),其次是Debian,再其次是 Win10。 可以看到,我最常用的Manjaro安装在相对低效的机械硬盘,最少用的Win10却安装在高效的固态硬盘。 于是我想将这几个系统都迁移到200GB固态硬盘上,以达到固态硬盘的充分利用。 在这迁移过程中牵涉到了UEFI、GRUB的知识,在学习之后,成功达到了这个目标。当之后整理了一下思路,想到了更高效的流程,现记录如下。

工具

  • gnome disk:用于查看分区情况;
  • gparted:用于将Debian和Manjaro所在分区缩小到尽量小,以缩短dd命令的执行时间,或腾出空间;
  • dd 命令:用于拷贝分区;
  • 硬盘盒:用于将硬盘的SATA接口转USB接口;

思路

将Debian和Manjaro系统的分区都拷贝到已经安装了Windows的硬盘,然后chroot到Manjaro分区,执行grub-install, update-grub命令。

具体步骤

1. 缩小Manjaro,Debian,Windows系统分区

启动旧电脑的Manjaro系统,将750GB机械硬盘、200GB固态硬盘插入硬盘盒,通过USB口连接到旧电脑。 打开gparted程序:

  • 将Debian、Manjaro系统的分区缩至最小
  • 将Windows系统的分区缩小到剩余10GB空白空间

2. 将Debian,Manjaro拷贝到固态硬盘

用gparted程序在固态硬盘的空白位置新建3个分区,分别用于放Debian、Manjaro、Swap分区,根据系统所需要的最小大小及使用频率设置分区空间。

使用dd命令将Debian分区拷贝到固态硬盘:

sudo dd if=/dev/sda* of=/dev/sdb bs=10MB status=progress

解释:status=progress表示拷贝过程显示速度和进度,bs=10MB以10MB为最小单位进行拷贝,默认是512bytes,也就是硬盘的一个sector的尺寸,但是实测这样速度较慢,可能是读写单位尺寸太小、读写频率太高,会降低读写速度的缘故。

之后用同样的命令将Manjaro分区拷贝到固态硬盘。

3. chroot 到Manjaro 分区,在ESP分区安装grub 程序,以及更新grub的配置文件1

sudo mount /dev/sdXXX /mnt
sudo mount /dev/sdXX /mnt/boot/efi
for i in /dev /dev/pts /proc /sys /run; do sudo mount -B $i /mnt$i; done
sudo chroot /mnt
grub-install /dev/sdX
update-grub 

解释:

  • 电脑启动时,需要boot loader加载操作系统,Linux一般使用grub作为 boot loader。
  • 电脑启动后时,首先UEFI硬件会加载ESP(EFI system partition) 分区内的文件,grub-install命令就是在这个分区内存入grub程序;
  • ESP分区内的grub程序被UEFI加载后,会再加载指定操作系统分区的的normal 模块,grub-install时会将操作系统分区的位置硬编码到grub程序,或者生成配置文件放在ESP分区。normal的路径通常是/boot/grub/x86_64-efi/normal.mod
  • normal模块加载其他模块,以及配置文件/boot/grub/grub.cfg,并会生成类似下图的界面2,让用户选择接下来要启动的系统。3列表项、背景、文字颜色等等都是在这个配置文件内指定,不过这个配置文件一般是由update-grub命令生成,而不直接对其进行编辑。update-grub会自动检查当前有哪些系统分区,生成这个操作系统列表。update-grub会加载/etc/default/grub, /etc/grub配置文件45,这个就是用户需要编辑的配置文件。我只需要update-grub生成操作系统列表就够了,不需要更改配置文件。

grub menu interface

另外:

  • 检查/etc/default/grub中是否用到的UUID是否有错,如果有错,则先改正然后执行update-grub命令;
  • 检查/etc/fstab是否有错。

4. 将固态硬盘安装到UEFI 的新电脑,启动,到此结束。

补充

1. BIOS, UEFI

在BIOS和UEFI这两种硬件下,启动的第一个阶段有很大不同,因此grub在这两种硬件下也有相应的不同:

  • BIOS启动后,先加载MBR,所以grub-install在MBR安装boot.img,但MBR太小,只要512bytes,无法做太多事情,所以它只作为跳板,加载后面的程序。也就是MBR与第一个分区的间隙,这个空间也不大,通常只有32KB,但是足够访问/boot/grub路径,加载其他模块(用于生成 grub menu、加载操作系统等功能的模块),grub-install在这里存放文件被称为core.img6
  • UEFI能够识别GPT分区表和FAT文件系统,启动后会会直接加载ESP分区,而不用像BIOS先从MBR跳转。UEFI从ESP分区加载其中的EFI 应用。grub-install在里面存放了grub程序,即grubx86.efi,它的作用和BIOS下的core.img作用一样,不过是按照EFI的接口进行编译的。7如果只安装了一个操作系统,则不用加载grub,UEFI可以直接加载操作系统。

2. Win10

grub没法直接启动Win10,需要chainload,即传递给Win10自己的EFI程序。安装Win10后,在ESP分区可以看到有Microsoft目录,里面是Windows的bootloader。执行update-grub生成的配置文件会处理好这件事情,从而开机进入menu interface时,选择windows系统,会将权限传递给windows的程序。8

查看grub.cfg配置文件的chainload部分,可以看到它加载了ESP分区下Microsoft目录的EFI程序:

menuentry 'Windows Boot Manager (on /dev/sda2)' --class windows --class os $menuentry_id_option 'osprober-efi-6DD8-DACA' {
        savedefault
        insmod part_gpt
        insmod fat
        set root='hd0,gpt2'
        if [ x$feature_platform_search_hint = xy ]; then
          search --no-floppy --fs-uuid --set=root --hint-ieee1275='ieee1275//disk@0,gpt2' --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hin
t-baremetal=ahci0,gpt2  6DD8-DACA
        else
          search --no-floppy --fs-uuid --set=root 6DD8-DACA
        fi
        chainloader /efi/Microsoft/Boot/bootmgfw.efi
}

反思

相比较而言,2种做事方式:

  • 定目标-》摸索-》遇到问题-》查文档-》再摸索-》总结

  • 定目标-》查文档-》定方案-》摸索-》总结

后一种可能效率要高得多。