前言
因为一些原因,我需要切换使用不同的内核版本,因而我硬盘里面有一份完整的linux git内核源码。
我安装了一个ubuntu20.04的虚拟机,它的内核是5.11。我以它作为母本,克隆生成虚拟机。在虚拟机中,编译安装上面的内核源码,以使用不同版本内核。
所以总体思路分为以下几步:
-
使用virt-manager管理虚拟机,搭建虚拟机母本。
-
virt-manager设置主机和虚拟机之间文件共享,linux源码位于共享文件夹中,为多个虚拟机提供服务。
-
Compiling a Custom Linux Kernel on a Virtual Machine,替换虚拟机的内核为指定版本。
在虚拟机上编译自定义 Linux 内核
在虚拟机中安装必要的软件包。
sudo apt install make gcc flex bison libssl-dev libelf-dev libncurses-dev
在主机中编译内核。
cd linux
sudo make mrproper
cp /boot/config-$(uname -r) ../linux_image/5.5/.config
# make defconfig O=../linux_image/5.5
make menuconfig O=../linux_image/5.5
make -j `nproc` O=../linux_image/5.5
# 将 CONFIG_SYSTEM_TRUSTED_KEYS="debian/canonical-certs.pem"
# 修改为
CONFIG_SYSTEM_TRUSTED_KEYS=""
将主机中的linux源码和编译之后的内核代码文件夹,共享到虚拟机中。因为编译之后的内核代码的部分文件会通过软连接的方式指向源码,比如source -> /mnt/data/linux
(source是编译生成的文件,其指向linux源码目录)。所以,我们需要将主机中的linux源码文件夹,以相同的位置,挂载在虚拟机中。这样软连接才能找见对应的源文件。
# 将主机中的linux源码挂在到虚拟机中。
# linux源码在主机中的绝对路径:/mnt/data/linux
# linux源码在虚拟机中挂载的绝对路径,应保持一致:/mnt/data/linux
sudo mount -t 9p -o trans=virtio /linux /mnt/data/linux
# 将编译生成内核代码挂载到 ~/image
sudo mount -t 9p -o trans=virtio /image ~/image
# 挂载其他需要的共享文件夹
# sudo mount -t 9p -o trans=virtio /cve ~/cve
更换虚拟机的内核版本
cd ~/image
sudo make modules_install -j `nproc`
sudo make install -j `nproc`
安装输出内容。
dacao@dacao-0:~/linux_image$ sudo make install -j `nproc`
sh /home/dacao/linux/arch/x86/boot/install.sh 5.5.0 arch/x86/boot/bzImage \
System.map "/boot"
run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 5.5.0 /boot/vmlinuz-5.5.0
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 5.5.0 /boot/vmlinuz-5.5.0
update-initramfs: Generating /boot/initrd.img-5.5.0
run-parts: executing /etc/kernel/postinst.d/unattended-upgrades 5.5.0 /boot/vmlinuz-5.5.0
run-parts: executing /etc/kernel/postinst.d/update-notifier 5.5.0 /boot/vmlinuz-5.5.0
run-parts: executing /etc/kernel/postinst.d/xx-update-initrd-links 5.5.0 /boot/vmlinuz-5.5.0
I: /boot/initrd.img.old is now a symlink to initrd.img-5.11.0-34-generic
I: /boot/initrd.img is now a symlink to initrd.img-5.5.0
run-parts: executing /etc/kernel/postinst.d/zz-update-grub 5.5.0 /boot/vmlinuz-5.5.0
Sourcing file `/etc/default/grub'
Sourcing file `/etc/default/grub.d/init-select.cfg'
正在生成 grub 配置文件 ...
找到 Linux 镜像:/boot/vmlinuz-5.11.0-34-generic
找到 initrd 镜像:/boot/initrd.img-5.11.0-34-generic
找到 Linux 镜像:/boot/vmlinuz-5.11.0-27-generic
找到 initrd 镜像:/boot/initrd.img-5.11.0-27-generic
找到 Linux 镜像:/boot/vmlinuz-5.5.0
找到 initrd 镜像:/boot/initrd.img-5.5.0
Found memtest86+ image: /boot/memtest86+.elf
Found memtest86+ image: /boot/memtest86+.bin
完成
修改grub配置文件,使得开机进入grub选择界面。
gedit /etc/default/grub
# 开机的时候,显示grub引导界面。默认停留10秒。超过10秒没有选择之后,默认选择第一个进入启动项。
# 注释掉 GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=10
sudo update-grub
开机在引导界面的高级选项中,选择需要的内核版本,进入系统。更多内容,可参考:How can I boot with an older kernel version?
为调试内核做的准备工作
- 添加内核调试信息的编译选项
make defconfig O=../linux_image/5.5 Kernel hacking ---> Compile-time checks and compiler options ---> [*] Compile the kernel with debug info
- 关闭地址随机化。类似于
qemu-system-x86_64
的-append 'nokaslr' \
sudo gedit /etc/default/grub # 在GRUB_CMDLINE_LINUX_DEFAULT或GRUB_CMDLINE_LINUX中,添加nokaslr # 我的配置是 GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" GRUB_CMDLINE_LINUX="locale=zh_CN nokaslr" <----------nokaslr为后期添加
错误处理
-
recipe for target 'certs' failed
参考:Attempting to compile kernel yields a certification error
将
.config
中的CONFIG_SYSTEM_TRUSTED_KEYS="debian/canonical-certs.pem"
,修改为CONFIG_SYSTEM_TRUSTED_KEYS=""
我不知道为什么需要这么修改,但确实是有效。
-
warning: the frame size of 1072 bytes is larger than 1024 bytes
参考:安卓驅動編譯warning: the frame size of 1072 bytes is larger than 1024 bytes
这是一个警告,我并没有修改它。这里仅记录下。
-
使用
make defconfig
默认生成的配置文件,编译安装之后的启动,在了"ubuntu loading initial ramdisk",卡住。关于
initial ramdisk
的相关内容,可参考:initrd 的重要性与创建新 initrd 文件-鸟哥私房菜我们在本章稍早之前『会需要initrd 的原因,是因为核心模块放置于/lib/modules/$(uname -r)/kernel/ 当中, 这些模块必须要根目录(/) 被挂载时才能够被读取。但是如果核心本身不具备磁碟的驱动程序时, 当然无法挂载根目录,也就没有办法取得驱动程序,因此造成两难的地步。
initrd 可以将/lib/modules/… 内的『启动过程当中一定需要的模块』包成一个文件(档名就是initrd), 然后在启动时透过主机的INT 13 硬件功能将该文件读出来解压缩,并且initrd 在内存内会模拟成为根目录, 由于此虚拟文件系统(Initial RAM Disk) 主要包含磁碟与文件系统的模块,因此我们的核心最后就能够认识实际的磁碟, 那就能够进行实际根目录的挂载啦!
系统在"ubuntu loading initial ramdisk"处停住,无法进入系统。可能是,使用默认配置编译生成的内核,
initrd.img
中缺少一些进入系统的必要模块。所以,这里,我使用当前系统内核的配置来编译生成新的内核:
cp /boot/config-$(uname -r) ../linux_image/.config
。编译安装之后,顺利进入新内核的系统。