目录
一 接续上文
二 编译根文件系统
三 构建完善根文件系统
四 内核中指定根文件系统
五 带根文件系统启动内核
在上一篇文章中,我们展示了通过QEMU仿真软件来运行Linux内核的过程。因为是仿真,所以我们可以构建不同架构平台的环境来进行调试,这对开发者学习内核非常友好。当然,为了简化过程且能够有一定代表性,平台选择了ARM。
学内核之一:基于QEMU搭建Linux内核启动调试环境_龙城赤子的博客-CSDN博客
显然,基于上文的环境,我们只能调试内核的启动过程。这是因为没有提供根文件系统,内核启动后,加载文件系统时就会异常停止,自然也无法执行shell等待用户输入。当我们想要调试一些运行过程中的内容时,就很不方便了,甚至是无法进行。比如,想看看一些系统调用的执行过程,或者设定一些触发条件,在事件产生时查看内核的处理逻辑等。
这这一篇文章中,我们就来补充缺失的根文件系统。这也算是对上一篇最后部分提出的第一个问题的解答。
在正式开始之前,对另外两个问题也做个简单的说明。
关于append参数,就是提供给内核的启动命令,效果类似uboot中传递给内核的cmdline。当然,有没有这个参数以及参数内容设置正确与否,都不影响启动过程。因为只有运行到使用参数的地方时,代码逻辑才会检测出问题。在本文中,这个参数不仅要有,而且还要正确,因为我们要加载根文件系统,让系统在模拟环境下完整运转起来。
关于GDB调试,其实我们可以将内核生成的vmlinux也看做是一个程序,那就好理解多了。vmlinux跟普通程序的区别是它的地址空间是确定下来了的。里面使用的地址也是加载后实际的地址。vmlinux.o文件则类似应用程序,使用的是相对地址。对于应用程序,可以由操作系统的加载过程完成地址的映射处理,而对于内核,就需要自己解决。
其次,GDB使用vmlinux可以明确调试过程中的符号信息。
好了,进入正题。
为了不让内核运行后挂起来,我们需要完成根文件系统的加载。这样,就需要提供一个根文件系统。对于嵌入式领域开发者来讲,busybox是流行选择。
从busybox官方网站下载一个稳定版。BusyBox
我这里下载了1.33.2版本。解压后,就可以考虑编译了。参照网上的资料,选择静态库方式。
make defconfig
make menuconfig
make CROSS_COMPILE=arm-none-eabi- ARCH=arm -j4
在menuconfig中选择静态方式 Build static binary
不过,在编译过程中出现了错误:
CC applets/applets.o
In file included from include/libbb.h:13,
from include/busybox.h:8,
from applets/applets.c:9:
include/platform.h:168:11: fatal error: byteswap.h: No such file or directory
168 | # include
| ^~~~~~~~~~~~
compilation terminated.
scripts/Makefile.build:197: recipe for target 'applets/applets.o' failed
make[1]: *** [applets/applets.o] Error 1
提示找不到byteswap.h头文件。在编译工具链目录下搜索了一下,确实没有上述头文件。具体是编译工具链自身问题还是跟busybox版本匹配问题,有时间了再研究。参考本人方法的读者,需要在这块注意了。
我选择了另一个工具链,这个是瑞星微开发板带的工具链,有上述头文件
./arm-buildroot-linux-gnueabihf/sysroot/usr/include/bits/byteswap.h
./arm-buildroot-linux-gnueabihf/sysroot/usr/include/byteswap.h
使用这个工具链,可以编译通过。所以,如果你也遇到了上述问题,可以考虑换个工具链。为确保工具链匹配,在编译之前,可以先检查一下是否包含上述头文件。另外需要注意的是,不同工具链的二进制兼容性也是要考虑的。像eabi、thumb、浮点支持情况等。这里也先留作一个问题,后续我们通过一篇新的文章来介绍于ARM相关的此类常见问题。
最终的编译工具链就换成了下面这个
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j4
编译完成后,直接make install可能会有问题,尽量带上工具链,确保万无一失
make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm install
完成后,会生成一个_install目录,里面包含了基础的命令。因为是静态方式,所以不包含库。
其实,生成的各种命令是符号链接,这样不仅可以缩小体积,还可以加速启动和执行过程。内核启动加速技术就有这相关的。感兴趣的读者可以搜索相关资料研究。
目前只是具备了启动程序和周边命令。我们还要补充文件系统相关的其他目录,包括dev、etc、proc、sys等。这里面有些是内核自动生成的,有些是手动或者执行特定命令创建的。本人没有花费太多时间,参考了网上例子。具体读者可以自己搜索,这部分问题不大。
然后将生成的文件系统目录下的所有文件打包
root@ubuntu:/home/work/KernelStudy/rootfs# find . | cpio -o --format=newc > rootfs.img
cpio: File ./rootfs.img grew, 1918976 new bytes not copied
7563 blocks
现在我们有了根文件系统,剩下只需做两件事,在内核配置中指定上述文件所在目录,其次在qemu命令行中指定上述文件所在位置。我们一件一件来搞定。
这里需要注意,如果直接在内核目录下menuconfig,并在其中配置上述配置项CONFIG_INITRAMFS_SOURCE="/home/work/KernelStudy/rootfs"
则编译内核时,会引出很多配置要求
如果你一路默认选择下来,并使用前一篇文章中指定的qemu命令启动新生成的内核时,将看不到任何输出。本人试了好几次,都是这样。每次make defconfig后直接编译的版本是可用的。为了规避这个问题(后续有时间再研究),我采用了另外一种迂回的办法,就是先make defconfig,然后打开生成的.config文件,直接修改里面的CONFIG_INITRAMFS_SOURCE项目,之后执行make bzImage -j4 ARCH=arm CROSS_COMPILE=arm-none-eabi-。此时虽然还会出现一些配置项,但是默认处理后,编译的内核是可以通过qemu启动的。
现在,我们在qemu命令中指定append附带的cmdline以及关联的根文件系统img文件。
qemu-system-arm -nographic -m 512M -M virt -kernel /home/work/KernelStudy/Kernel/linux-4.19.244/arch/arm/boot/zImage -append "rdinit=/linuxrc root=/dev/ram console=ttyAMA0 loglevel=8" -initrd /home/work/KernelStudy/rootfs/rootfs.img
之后,内核启动起来。且cmdline也传递进去了
最后,成功加载根文件系统
我们看到,命令可以使用
在此基础上,我们就可以调试运行状态的内核了。关于GDB调试内核过程及前面提到的问题,我们下一篇再讲。今天就到这里。
JUST DO IT
ENJOY IT