Skip to content

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 ,但这样我就很难去记录我依赖了哪些包(相当

Linux kernel - NixOS Wiki

nix文档中还提到了这两步

[nix-shell] $ unpackPhase && cd linux-*
#此步骤为解压内核源码,由于我是使用自己下载的 tarball,故不需要执行

[nix-shell] $ patchPhase
#这部分为执行 patch ,我尝试执行,其操作大部分都是 #!/bin/sh 替换成 nixos 的格式,故该步骤也不需要执行

不得不说,靠查 menuconfig 看不懂的东西可以了解好多奇奇怪怪的硬件和新的内核特性。

shell
$ 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

在弹出的窗口里面出现了这样的报错

error image

很显然,根据报错,缺了一个 rootfs 让内核去启动.

接下来需要制作一个 initramfs

集成基础的软件包,制作 LiveCD

最简单的方法是选用现有的 rootfs 并在其基础上进行修改 我们可以通过 docker export 导出 docker 镜像的 rootfs 我一开始尝试提取 ubuntu:18.04rootfs ,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"

img 得到了一个这样的报错,此时距离一个可以正常引导的发行版距离还是有点远

init 的作用,以及如何实现一个 init?

  • 通常 init 位于 /bin/init 它会在内核引导以后以 PID 1 长期存在,同时也会接收系统的控制请求,并完成一些操作

那么它到底做了哪些?以及它到底需要完成什么工作呢

  • 幸运的是, 答案早就写在了代码里

这里以 busybox 提供的 init. 为例子

busybox/init.c at master · brgl/busybox · GitHub

C
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 这时便进入了单用户模式 总之,它已经可以启动了。

img

这个发行版也应该有一个自己的名字,

暂且将其称为 Mahiro OS

接下来为了命名它,需要修改 /etc/hostname ,目前它是个空文件

  • 在真实的系统里面,它通常是用于网络通信时的主机名,而对于发行版,往往是 /etc/lsb-release 或者 /etc/os-release 这类约定俗成的文件
    为了修改磁盘上的文件,
    首先需要挂载 root 为 rw
shell
mount -o rw,remount /dev/sda /  
hostname Mahiro-OS
uname -a

img 好的,现在它已经有了自己的名字

借物表: Manually booting the Linux kernel inside QEMU :: The M47r1x — The Return of Neo