linux学习3,手把手教你运行编译的内核,五分钟让linux跑起来
上一节介绍了如何编译出指定平台的 linux 内核,也介绍了如何安装和利用 qemu 模拟器运行编译出来的 linux 内核。在此基础上,我们尝试修改了 linux 内核源码,成功的让 linux 内核在启动时,打印出了我们的名字。
我不明白,上一篇是一个一个字手打出来的原创文章,为何头条给了 0 推荐。感兴趣的朋友手动点我过去看看吧。
还记得上一节遗留的问题吗?虽然 qemu 模拟器成功的运行了 linux 内核,但是内核启动后,因为找不到文件系统,陷入“kernel panic(内核恐慌)”。本节,我们一起来解决这个问题,让 qemu 真正的运行起 linux 系统。
我们计划在一台电脑上模拟运行我们编译的 linux 内核,那文件系统放在哪呢?再新建一个磁盘分区太浪费了吧?好在我们的主机是 linux 系统,可以新建一个文件用于模拟磁盘分区。执行下面这条命令:
dd if=/dev/zero of=disk.img bs=20k count=25600
这条命令的意思是,在当前目录新建一个空文件 disk.img,这个空文件的大小等于 20K x 25600,也即约 524MB。现在 disk.img 文件是个空文件,相当于一块没有格式化的硬盘,所以如果想让它做 linux 的文件系统,首先就要把它格式化为 linux 内核能够识别格式,这里选择 ext2 格式:
mkfs -t ext2 disk.img
格式化可能需要我们输入 y。命令执行完毕,我们就得到了“一块 ext2 格式的硬盘”。我们新建一个目录 rootfs,把它挂在到这个目录上。
mkdir rootfs sudo mount -o loop disk.img rootfs ls rootfs
现在 rootfs 里面的内容就是 disk.img 的内容。我们将 linux 内核的一些库安装到这个目录:
cd linux-2.6.26 make modules_install INSTALL_MOD_PATH=../rootfs
执行上面的命令后,发现库已经被安装在我们的“虚拟磁盘”里了。
我们卸载 rootfs,把改动刷新到 disk.img
cd .. sudo umount rootfs
接下来,为了测试方便,我们先把内核镜像拷贝到当前目录:
cp linux-2.6.26/arch/x86_64/boot/bzImage .
上一节,我们使用 qemu 模拟运行 linux 内核时,内核找不到文件系统陷入恐慌,那我们现在用 disk.img 作为文件系统,看看会怎样:
qemu-system-x86_64 -m 512M -smp 1 -kernel bzImage -drive format=raw,file=disk.img # 指定文件作为磁盘 -append "root=/dev/hda" # 内核启动参数,指定根文件系统所在设备 -curses
执行这条命令,发现 linux 内核启动了,但是最后还是陷入“内核恐慌”:
不过这次不是因为找不到文件系统了,而是因为找不到 init 程序,而且也找不到 console。linux 内核在启动差不多完成时,需要一个 init 程序,用于做根目录挂载等一些初始化工作,一些终端信息会通过 console 打印出来。现在 linux 内核找不到 init 程序,完成不了工作,自然“恐慌”的一批。那我们给他指定一个 init 程序,再创建一个 console 就好了嘛。
我们先用 c语言 写一个 init 程序:
#include <stdio.h> int main() { printf(" hello, i am init program! "); return 0; }
然后编译之:
gcc -static -o init init.c
这样就得到了 init 程序。然后,再把 disk.img 挂载到 rootfs 上,因为我们需要把 init 放进去。放进 init 之后,还要创建 dev 目录,并且在里面创建 console:
sudo mount -o loop disk.img rootfs cp init rootfs cd rootfs mkdir dev mknod dev/console c 5 1 cd .. sudo umount rootfs
上面的最后一条命令是卸载 disk.img,目的是把改动刷新到 disk.img 里,好了一切都改动好了,现在再来运行一次试试:
qemu-system-x86_64 -m 512M -smp 1 -kernel bzImage -drive format=raw,file=disk.img -append "init=/init root=/dev/hda" -curses
发现我们的 init 程序被内核启动了,而且 linux 内核找到 console,并把信息打印出来了,但最后还是报错了。这是肯定的,因为我们指定的 init 程序除了打印信息,什么也没做。真正能把 linux 内核启动起来的 init 程序还是挺复杂的,当然,这些工作有人已经完成了,例如 busybox。限于篇幅,下一节再介绍如何真正的把我们自己编译的linux 内核启动起来。
欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。