Mahiro OS 来试着动手做一个发行版
项目目的:学习和了解发行版构造,在实际的环境中运行发行版,并跑起来自己的服务
不适合直接用于生产目的
项目起因:从事嵌入式开发,用于任务目的打包 Linux 固件的次数非常多,并且在这之前已经了解了一些构建系统相关的内容。但一直对很多细节没有概念。 同时,拿我已经熟悉的框架搭建博客这件事也很难从中学到一些新的东西,对我来讲更像是重复无意义的工作。 近一段时间的工作我在大量的处理各种 Makefile , 并且对实际工作中用到的工具链有了些更深的体会,所以我决定自己动手搭一个发行版。
需要实现的工作:
- ✔ 编译并启动一个主线内核
- ✔ 集成基础的软件包,制作 LiveCD,跑起来看看
- ⭕ 真实的服务器环境下运行
- ⭕ 实现基本的包管理
- ✔ 启用容器化
- ✔ 部署一个 Web 服务
拉取内核,编译并启动
在这方面,可以参考Linux From Scratch - Version 11.2 我以前也有使用 buildroot 类的工具 LFS 也提供了做了处理的 Kernel,但这样就不符合学习的目的了,所以我准备从主线内核开始。
1 .1 环境参考
编译环境:R9 5900S 笔记本 + Nix 这里使用到的发行版并非 NixOS,常见发行版可以参照 nix | 镜像站使用帮助 | 北京外国语大学开源软件镜像站 | BFSU Open Source Mirror 安装 Nix .
1.2 构建系统准备
构建系统的话,这里准备使用 nix 进行。 其使用和参考可以去查看 nix 教程(并不是nixos) 一开始的想法是直接使用 Ubuntu ,但这样我就很难去记录我依赖了哪些包(相当
nix文档中还提到了这两步
[nix-shell] $ unpackPhase && cd linux-*
#此步骤为解压内核源码,由于我是使用自己下载的 tarball,故不需要执行
[nix-shell] $ patchPhase
#这部分为执行 patch ,我尝试执行,其操作大部分都是 #!/bin/sh 替换成 nixos 的格式,故该步骤也不需要执行
不得不说,靠查 menuconfig 看不懂的东西可以了解好多奇奇怪怪的硬件和新的内核特性。
$ nix-shell -E 'with import <nixpkgs> {}; linux.overrideAttrs (o: {nativeBuildInputs=o.nativeBuildInputs ++ [ pkg-config ncurses openssl_1_1];})'
(nix-shell $) make menuconfig
(nix-shell $) make -j16
#接下来等待内核编译完成就行,此处也可以 -j数字 启动多线程编译。
```Shell
Kernel: arch/x86/boot/bzImage is ready (#1)
[nix-shell:~/linux-6.1.7]# file arch/x86_64/boot/bzImage
arch/x86_64/boot/bzImage: symbolic link to ../../x86/boot/bzImage
[nix-shell:~/linux-6.1.7]# ls -al arch/x86/boot/bzImage
-rw-r--r-- 1 root root 8431936 Jan 20 23:14 arch/x86/boot/bzImage
[nix-shell:~/linux-6.1.7]# file arch/x86/boot/bzImage
arch/x86/boot/bzImage: Linux kernel x86 boot executable bzImage, version 6.1.7 (root@Moxi-GA401) #1 SMP PREEMPT_DYNAMIC Fri Jan 20 23:13:57 CST 2023, RO-rootFS, swap_dev 0X8, Normal VGA
最开始我以为只编译了 X86 的 Kernel,随后我检查产物目录,发现 X86-64 的被软连接到了 X86.
关于如何启动这个 Kernel ,这里涉及到了引导,会在 LiveCD 那里写明。
但在这之前也可以简单的在虚拟环境中把它跑起来。
这里采用 QEMU 模拟启动内核,大部分发行版都可以通过自带的包管理去安装 QEMU
qemu -kernel arch/x86/boot/bzImage
在弹出的窗口里面出现了这样的报错
很显然,根据报错,缺了一个 rootfs 让内核去启动.
接下来需要制作一个 initramfs
集成基础的软件包,制作 LiveCD
最简单的方法是选用现有的 rootfs 并在其基础上进行修改 我们可以通过 docker export
导出 docker
镜像的 rootfs
我一开始尝试提取 ubuntu:18.04
的 rootfs
,tree 输出为 其包含 584 directories, 2577 files
观察发现其中还有 i18n 支持 故而选择 alpine 作为基础参考 其包含 105 directories, 424 files
创建一个空的磁盘镜像,并将其用 mke2fs 使其变为一个 ext2 设备镜像
dd if=/dev/zero of=/tmp/root.img bs=1024k count=64
mke2fs /tmp/root.img
mount /tmp/root.img /mnt/root
在 QEMU 中把这个磁盘添加上去
qemu --append "root=/dev/sda" -hda "/tmp/root.img" -kernel "/tmp/bzImage"
得到了一个这样的报错,此时距离一个可以正常引导的发行版距离还是有点远
init 的作用,以及如何实现一个 init?
- 通常 init 位于
/bin/init
它会在内核引导以后以 PID 1 长期存在,同时也会接收系统的控制请求,并完成一些操作
那么它到底做了哪些?以及它到底需要完成什么工作呢
- 幸运的是, 答案早就写在了代码里
这里以 busybox 提供的 init. 为例子
busybox/init.c at master · brgl/busybox · GitHub
if (parser == NULL)
#endif
{
/* No inittab file - set up some default behavior */
/* Sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, "");
/* Askfirst shell on tty1-4 */
new_init_action(ASKFIRST, bb_default_login_shell, "");
//TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
/* Reboot on Ctrl-Alt-Del */
new_init_action(CTRLALTDEL, "reboot", "");
/* Umount all filesystems on halt/reboot */
new_init_action(SHUTDOWN, "umount -a -r", "");
/* Swapoff on halt/reboot */
new_init_action(SHUTDOWN, "swapoff -a", "");
/* Restart init when a QUIT is received */
new_init_action(RESTART, "init", "");
return;
}
那直接使用 busybox 提供的 init 是否可行呢,答案是可以的 在启动过程中,如果 kernel 没找到 bin/init 则会去尝试 /bin/sh 这时便进入了单用户模式 总之,它已经可以启动了。
这个发行版也应该有一个自己的名字,
暂且将其称为 Mahiro OS
接下来为了命名它,需要修改 /etc/hostname
,目前它是个空文件
- 在真实的系统里面,它通常是用于网络通信时的主机名,而对于发行版,往往是
/etc/lsb-release
或者/etc/os-release
这类约定俗成的文件
为了修改磁盘上的文件,
首先需要挂载 root 为 rw
mount -o rw,remount /dev/sda /
hostname Mahiro-OS
uname -a
好的,现在它已经有了自己的名字
借物表: Manually booting the Linux kernel inside QEMU :: The M47r1x — The Return of Neo