从内核到可启动镜像:0到1构建你的极简Linux系统

90 阅读6分钟

从内核到可启动镜像:0到1构建你的极简Linux系统

关注微信公众号:Linux内核拾遗

文章来源:mp.weixin.qq.com/s/y27zD6DPY…

一、理解Linux系统层级

Linux生态系统由内核(Kernel)和用户空间(User Space)共同构成。内核作为操作系统核心,直接管理硬件资源和基础服务(进程调度、内存管理、设备驱动等),而用户空间则包含应用程序、系统工具和运行环境。主流通用发行版(如Ubuntu、CentOS)的本质是在标准内核之上,通过整合以下组件形成完整系统:

  • 基础工具链:GNU Coreutils、gcc、glibc等
  • 服务管理:systemd或SysVinit
  • 软件分发:APT/YUM/DNF包管理系统
  • 硬件适配:DKMS动态内核模块支持

本文聚焦于构建一个最小化可启动镜像,该镜像包含定制编译的内核、精简的临时根文件系统(initramfs)和基础Shell环境,揭开Linux操作系统的神秘面纱。

二、编译Linux内核

1. 环境准备与源码获取

我们需要准备好构建环境,这里以Ubuntu 22.04为例。首先安装内核编译过程中需要用到的依赖包,然后从Linux kernel官网获取内核源代码。

# 安装编译依赖(Ubuntu/Debian)
sudo apt update && sudo apt install -y \
    build-essential \
    libncurses-dev \
    flex \
    bison \
    libssl-dev \
    libelf-dev \
    git

# 获取内核源码
git clone --depth 1 \
    https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
cd linux

如果因为某些魔法导致内核下载很慢,可以尝试使用国内的镜像源,例如清华大学的开源镜像站(mirrors.tuna.tsinghua.edu.cn/help/linux.…

# 从清华开源镜像站获取内核源码
git clone --depth 1 https://mirrors.tuna.tsinghua.edu.cn/git/linux.git

接下来我们默认使用Linux内核主干分支进行内核编译,如果需要指定版本的话,可以git clone的时候使用-b <版本名>来指定,例如-b v6.14,或者是git clone完了之后切换到对应的tag也行,例如git checkout v6.14

2. 内核配置和优化

接下来是内核编译前的一些配置工作,我们可以在默认配置的基础上做一些调整:

# 生成默认配置
make defconfig

# 关键配置调整(可选,看个人需要)
scripts/config \
    --disable DEBUG_INFO \         # 禁用调试符号(减少体积)
    --enable VIRTIO \              # 启用虚拟化驱动
    --disable MODULES \            # 禁用动态模块加载
    --enable STATIC_KEYS \         # 优化内核功能开关
    --enable BPF_JIT               # 支持eBPF

# 查看配置变更
diff -u .config.old .config

3. 内核编译

前面准备工作做好之后,就可以愉快地开始我们的内核编译了:

# 并行编译(根据CPU核心数调整)
make -j$(nproc) 

这个过程比较长,取决于编译环境的核心数,耐心等待即可。过程中如果碰到依赖包缺失,按照错误提示安装然后重新编译即可,或者其他稀奇古怪的错误,直接google一下。这里不再赘述。

最后编译得到内核镜像文件:

  • x86_x64环境:arch/x86_64/boot/bzImage
  • arm64环境:arch/arm64/boot/Image.gz

三、构建最小化initramfs

内核编译好后,就可以开始制作我们的镜像根文件系统了。

1. 什么是initramfs

initramfs(Initial RAM File System)是 Linux 系统启动过程中使用的 临时根文件系统,它存储在内存中,用于在内核加载后、挂载真实根文件系统前完成关键初始化任务。

它区别于传统的initrd方案:

  • initrd(旧方案)
    • 基于磁盘映像(如ext2格式)。
    • 需单独分区或文件。
    • 效率较低,已逐步被淘汰。
  • initramfs(现代方案)
    • 基于cpio+压缩,直接嵌入内核或作为独立文件。
    • 通过tmpfs在内存中运行,无需块设备驱动。
    • 支持按需加载文件,更灵活高效。

2. 构造目录结构与设备节点

首先需要构造最基本的根文件系统目录结构和设备节点,例如主流Linux发行版中最常见到的bin、dev、proc、sys等目录,以及console、null等常用设备节点:

mkdir -p initramfs/{bin,dev,proc,sys}
cd initramfs

# 创建设备文件
sudo mknod dev/console c 5 1    # 控制台设备
sudo mknod dev/null c 1 3       # 空设备
sudo chown root:root dev/{console,null}

3. 集成BusyBox

BusyBox 是一个高度优化的 多合一 Unix 工具集,它将数百个常用 Unix 工具(如 lscatgrepmountifconfig 等)集成到一个精简的二进制文件中。

为了让我们自制的Linux系统在启动后能够执行基本的一些操作,可以将busybox集成到我们的镜像文件中。

# 下载并编译
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1

# 配置为静态链接
make menuconfig
# 进入配置界面后,请用以下选项:
Settings  --->
  [*] Build static binary (no shared libs)  # 静态编译

# 编译busybox
make -j$(nproc)

# 部署到initramfs
make install CONFIG_PREFIX=</path/to/initramfs>

这里说明一下,我们选择静态方式编译busybox,目的是将程序的所有依赖库直接打包进二进制文件,避免了Linux系统运行时依赖动态库。

4. 编写初始化脚本

根文件系统准备好之后,我们还需要添加一个初始化脚本“init”。它作为Linux 启动镜像(initramfs)中的 首个用户态进程(PID 1),由内核直接启动,承担着从内核过渡到用户空间的关键桥梁作用,主要任务是为后续系统启动准备必要的运行环境。

cat > init <<'EOF'
#!/bin/sh

# 挂载虚拟文件系统
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev

# 启动交互Shell
exec /bin/sh
EOF
chmod +x init

5. 打包与验证

至此,我们的根文件系统已经全部准备完成,最后将其打包到initramfs.img镜像文件中。

find . | cpio -H newc -o | gzip > ../initramfs.img

# 检查打包结果
file ../initramfs.img 
# 应显示类似内容:../initramfs.tar.gz: gzip compressed data, from Unix, original size modulo 2^32 2206208

四、制作可启动ISO镜像

1. 什么是ISO文件

ISO文件是一种标准的光盘镜像格式(遵循ISO 9660文件系统规范),通常用于封装完整的可启动操作系统或软件集合。在Linux系统构建中,ISO文件的核心作用是将内核(vmlinuz)、初始内存盘(initramfs.img)和引导程序(如GRUB)等关键组件打包成一个可物理刻录(光盘/USB)或虚拟机加载的独立镜像。

  1. 引导程序(如GRUB)从ISO中加载内核和initramfs.img到内存;
  2. 内核解压并运行initramfs.img中的临时根文件系统,执行硬件初始化、驱动加载等任务;
  3. 最终挂载ISO内的持久化文件系统(如Live CD的squashfs)或外部存储设备,完成系统启动。

2. 构建ISO目录结构

这里主要是构建boot目录,然后将前面编译或者构建好的内核镜像文件(bzImage或者Image.gz)、根文件系统镜像文件(initramfs.img)添加到boot目录中,然后配置参数(grub.cfg):

mkdir -p iso/boot/grub

# x86_64
cp ../linux/arch/x86_64/boot/bzImage iso/boot/vmlinuz
# or arm64
cp ../linux/arch/arm64/boot/bzImage iso/boot/vmlinuz

cp ../initramfs.img iso/boot/initrd

# 编写GRUB配置
cat > iso/boot/grub/grub.cfg <<'EOF'
menuentry "Custom Linux" {
    linux /boot/vmlinuz console=ttyS0 root=/dev/ram0 rw
    initrd /boot/initrd
}
EOF

3. 生成ISO文件

最后借助grub-mkrescure工具生成ISO镜像文件(custom-linux.iso):

# 安装工具依赖包
apt install mtools xorriso

# 生成ISO
grub-mkrescue -o custom-linux.iso iso/

# 验证ISO结构
xorriso -indev custom-linux.iso -ls

五、镜像测试

这里我们以VMWare为例来验证我们制作的Linux系统ISO镜像:

1、创建虚拟机,选择我们制作好的ISO镜像文件(custom-linux.iso):

image-20250330102353168

2、选择操作系统的配置,按需选择即可:

image-20250330102509267

3、完成虚拟机创建:

image-20250330102538422

4、启动虚拟机:

image-20250330102642567

image-20250330102703050

5、验证基本的busybox命令:

image-20250330102758561

最终我们得到了一个最简单的Linux系统镜像,还能够支持简单的busybox命令!

六、迈向真正的Linux发行版

前面我们只做的Linux系统镜像,距离真正的Linux发行版还有很大一段路要走,但是好消息是我们已经掌握了最核心的方法步骤,并且得到了一个可以在虚拟机环境下运行和验证的ISO镜像,我们可以在此基础上逐步去完善我们的镜像,最后得到一个完整的发行版。

  1. 建立基础运行时环境:移植动态链接库(glibc)、时区数据、基础配置文件(/etc/passwd)等。
  2. 实现包管理系统:
    • 设计元数据格式(包名、版本、依赖)。
    • 编写安装脚本(pre/post install hooks)。
    • 创建本地软件仓库。
  3. 添加网络与SSH服务。
  4. 构建自动化工具链:使用Makefile或Shell脚本实现内核编译、文件系统打包、ISO生成的流水线。
  5. 支持持久化存储:开发分区工具,支持ext4/XFS文件系统初始化,实现根文件系统切换。
  6. 创建安装程序:开发交互式CLI工具,支持磁盘分区、软件包选择、引导配置。

关注微信公众号:Linux内核拾遗

文章来源:mp.weixin.qq.com/s/y27zD6DPY…