上一节介绍了如何编译出指定平台的 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("\nhello, i am init program!\n\n");
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 内核启动起来。
博主你好,你的宿主机器环境是怎么样的(ubuntu多少的版本),能不能发一下, 我用ubuntu20.04 init那个阶段就无法一直成功?
当初写这系列文章时,用的应该是 16.04