实验目的
- 编译linux内核,并在qume虚拟机中运行。
实验原理
Linux内核启动原理:
系统上电之后,通过BIOS初始化硬件,定位Bootloader. Bootloader(引导加载程序) 是一种特殊的小程序,它负责在BIOS初始化后,从存储设备中加载操作系统内核 并把控制权交给内核。随后内核完成初始化、挂载根文件系统(这是什么,为什么要这个)。随后运行/init 程序程序。流程如图所示
+-------------------+
| 1. 上电 / 重启 |
+--------+----------+
|
v
+--------+----------+
| 2. BIOS / UEFI | ← 初始化硬件,定位Bootloader
+--------+----------+
|
v
+--------+----------+
| 3. Bootloader | ← 加载内核和initrd,传递参数
+--------+----------+
|
v
+--------+----------+
| 4. Linux 内核 | ← 初始化内核、挂载根文件系统
+--------+----------+
|
v
+--------+----------+
| 5. /init 程序 | ← 第一个用户态进程(PID 1)
+--------+----------+
Bootloader程序的initrd是什么?
系统内核启动过程
Linux 启动过程中,内核完成自身初始化(设备驱动、内存管理、调度器等)后,要进入“用户空间”,运行第一个用户程序,即 init(PID 1)程序;这个 init 通常负责启动系统服务,初始化系统,进入 login 界面等;但由于 init 是一个用户程序,必须存放在某个文件系统中,内核必须先挂载文件系统才能找到它。
那么如何挂在文件系统呢? 在grub(Bootloader的一种)中提供了一个选项“root=”用来指定第一个文件系统,告诉内核要挂载哪个分区作为根文件系统(rootfs)。问题是,有时候这个文件系统是在 USB、SCSI、NVMe 等设备上;而内核需要这些设备的 驱动程序 才能访问它们;可是这些驱动程序本身又存在于文件系统里 —— 就是你还没挂载的那个文件系统;这样就形成一个悖论:“要访问文件系统,需要驱动;而要加载驱动,需要先访问文件系统”。
解决方案是:创建一个临时根文件系统,有文件系统,有cache,放在内存中(RAM disk);启动时,内核把这个 RAM disk 文件系统先挂载起来,从里面加载驱动模块、init 脚本等;就是我们说的 initrd(initial RAM disk)机制;挂载完 RAM disk 后,init 脚本运行,负责加载硬盘驱动、挂载真实的根文件系统,然后切换过去。
但是,这种RAM disk的方案(下称initrd)虽然解决了问题但并不完美。 比如,disk有cache机制,对于RAM disk来说,这个cache机制就显得很多余且浪费空间;disk需要文件系统,那文件系统(如ext2等)必须被编译进kernel而不能作为模块来使用。
Linux 2.6 kernel提出了一种新的实现机制,即initramfs(RAM filesystem)。它不是模拟磁盘,而是使用 Linux 的 ramfs/tmpfs 内存文件系统;它本质上是一个 cpio 格式归档文件(压缩格式的一种),里面打包了 /init 脚本、必要的驱动、命令工具等;启动时,内核自己解压并加载它为 rootfs,无需挂载、无需缓存机制、也无需预置文件系统驱动;非常简洁高效,是现代 Linux 启动默认使用的机制。
initramfs工作机制
bootloader加载内核镜像文件与initramfs,内核解压 cpio 格式的 initramfs,加载为初始根文件系统,然后内核寻找 /init 文件并运行它(通常是 shell 脚本);这个脚本加载驱动、挂载真正的磁盘根文件系统,然后用 exec chroot . /sbin/init 切换过去;从此进入真实系统,initramfs 文件系统可以被释放。
实验过程
实验准备

请使用Ubuntu 22.04,不要使用Ubuntu 24,因为我尝试发现BusyBox暂时还没有支持Ubuntu24的

请自行下载这两个压缩包
解压文件
tar -jxvf busybox-1.37.0.tar.bz2
tar -Jxvf linux-source-6.1.tar.xz
tar 压缩命令:https://www.runoob.com/linux/linux-comm-tar.html
编译文件
编译linux文件
make menuconfig
make menuconfig 是图形化辅助生成.config文件的命令,这里我们直接Save

编译并生成bzImage镜像文件
make -j$(nproc) bzImage

bzImage 是一种用于 Linux 内核启动的映像文件格式,全称为 Binary Bootable Image。可以用来指导完成内核设置(Kernel Setup),内核初始化(Kernel Initialization)等。
编译busybox
make menuconfig
Setttings 选中 Build static binary (no shared libs), 使其编译成静态链接的文件, 因为bzImage内核本身不提供glibc

执行make install -j$(nproc), 当前文件夹会出现名为_install的文件夹。(有warning不用管,能跑就行。。。)

收尾工作
编写init程序
touch init
#!/bin/sh
echo "INIT SCRIPT"
mkdir /proc
mkdir /sys
mount -t proc none /proc
mount -t sysfs none /sys
mkdir /tmp
mount -t tmpfs none /tmp
echo -e "\nThis boot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
exec /bin/sh
sudo chomd +x init
将_install 打包为cpio文件
find . | cpio -o --format=newc > ../rootfs.cpio
--format=newc 指定打包文件的格式为 newc。newc 是一种常见的 cpio 格式,它支持长文件名和一些扩展属性,适用于 Linux 系统。
> 是重定向符号,用于将命令的输出保存到指定的文件中。
../rootfs.cpio 表示将打包后的文件保存到当前目录的上一级目录中,文件名为 rootfs.cpio。

编写startup.sh
GNU nano 6.2 startup.sh qemu-system-x86_64 \
-kernel /home/sun/oslab2/linux-source-6.1/arch/x86/boot/bzImage \
-initrd /home/sun/oslab2/busybox-1.37.0/rootfs.cpio \
--append "nokaslr root=/dev/ram init=/init console=ttyS0 " -nographic
执行./startup.sh

成功启动!!!
GDB 完成内核调试
重新编译内核
之前编译内核的时候,我们直接选择save,linux默认生成不带有调试信息的内核。现在需要修改linux配置, 导航到 Kernel hacking → Compile-time checks and compiler options → Debug information,选择Generate DWARF Version 5 debuginfo.之后重新make -j$(nproc)

检查vmlinux文件
在内核源码文件夹下有一个名为vmlinux的文件, 是内核的符号表, 用的上的用不上的都在里面。
file /home/sun/oslab2/linux-source-6.1/vmlinux
可以看到,有with debug_info

执行下面一行,后面有用
readelf -h /home/sun/oslab2/linux-source-6.1/vmlinux | grep Entry

修改startup文件
加入-s -S
qemu-system-x86_64 \
-kernel /home/sun/oslab2/linux-source-6.1/arch/x86/boot/bzImage \
-initrd /home/sun/oslab2/busybox-1.37.0/rootfs.cpio \
-append "nokaslr root=/dev/ram init=/init console=ttyS0" \
-s -S \
-nographic

开始调试
在一个终端执行./startup之后,在另一个终端执行
gdb /home/sun/oslab2/linux-source-6.1/vmlinux
target remote :1234
break start_kernel
continue

完成内核调试!!!
学习资料
USTC-LAB(1)差一个e怎么差这么多,脸都丢尽了
Comments NOTHING