当前位置: 首页 > news >正文

从零构建:深入理解Linux启动过程

这篇文章主要是后面博客的总结,使用的例子也来自于这篇博客 https://www.linkedin.com/pulse/kernel-rootfs-where-linux-actually-begins-moon-hee-lee-0fjqc/

Linux启动的三大核心组件

当我们深入理解Linux系统启动过程时,会发现它远比简单的"开机即用"要复杂。一个完整的Linux启动过程实际上是由三个关键组件精密协作的结果:

内核 (Kernel)

内核是Linux系统的核心,负责硬件抽象、进程管理、内存管理、设备驱动等底层功能。它是整个系统的"大脑",直接与硬件交互。

根文件系统 (RootFS)

RootFS包含了用户空间的所有组件,包括:

  • 系统管理器 (systemd)
  • 包管理工具 (apt/dpkg)
  • 核心系统库
  • 用户程序和配置文件
    它是系统运行的基础环境。

初始内存文件系统 (Initramfs)

Initramfs是一个临时的微型根文件系统,它在内核启动的早期阶段被加载。它的核心作用是:

  • 为内核提供必要的驱动模块
  • 检测并挂载真正的根文件系统
  • 完成系统切换后移交控制权给真正的init系统

启动流程的精妙协作

Linux系统的启动流程是一个精心设计的接力过程:

graph LRA[Kernel] --> B[Initramfs]B --> C[挂载真正RootFS]C --> D[switch_root]D --> E[Systemd/Init]

为什么需要Initramfs?

一个常见的问题是:为什么内核不直接加载RootFS,而是需要一个中间的Initramfs?

  1. 硬件支持:现代系统需要复杂的驱动程序来访问存储设备,这些驱动可能需要从网络或特殊存储加载
  2. 模块化设计:允许系统在真正根文件系统挂载前完成硬件初始化
  3. 灵活性:支持不同的存储设备类型和网络启动场景
  4. 错误恢复:提供系统诊断和修复的临时环境

Initramfs的核心职责

Initramfs在这个启动过程中承担着至关重要的职责:

  • 驱动程序加载:加载访问根设备所需的内核模块
  • 设备检测:识别和初始化存储设备(如SCSI、NVMe、virtio等)
  • 文件系统挂载:找到并挂载真正的根文件系统
  • 环境准备:为最终系统切换准备必要的环境
  • 控制权移交:通过switch_root切换到真正的根文件系统并启动init系统

这种设计确保了Linux系统能够在各种硬件环境下稳定启动,即使根文件系统位于复杂的存储设备或通过网络访问也能正常工作。

Prerequisite

工具安装

在进行Linux系统构建实验前,我们需要安装必要的开发工具链。这些工具涵盖了从内核编译、文件系统创建到虚拟化模拟的完整构建流程。

sudo apt updatesudo apt install -y \git build-essential bc bison flex libssl-dev libelf-dev dwarves \pahole openssl cpio rsync xz-utils zstd kmod \debootstrap qemu-system-x86 qemu-utils e2fsprogs

内核构建工具:

  • build-essential、bc、bison、flex - 内核编译必需的基础工具
  • libssl-dev、libelf-dev、dwarves、pahole - 内核模块开发和调试依赖
  • openssl - 加密和哈希函数支持
    文件系统工具:
  • debootstrap - 创建Debian兼容的根文件系统
  • cpio、rsync、xz-utils、zstd - 文件系统打包和压缩
  • e2fsprogs - ext4文件系统工具
    虚拟化工具:
  • qemu-system-x86、qemu-utils - x86架构虚拟化支持
  • kmod - 内核模块管理工具

工作区

创建工作区:

mkdir -p ~/linux-boot-lab/{src,build,artifacts/kernel,artifacts/modules}cd ~/linux-boot-lab

目录规划说明:

linux-boot-lab/  
├── src/ # 内核源码存储目录  
├── build/ # 临时构建工作区  
└── artifacts/ # 最终产物存储  ├── kernel/ # 内核镜像和配置  └── modules/ # 内核模块文件

Build the Kernel

编译内核大致分为几个步骤:

  1. 下载内核源码
  2. 配置内核
  3. 编译
  4. 安装模块

下面是详细的步骤和命令

下载内核源码

从官方仓库获取最新的Linux内核源码,使用--depth=1仅获取最新版本以节省时间和空间:

cd ~/linux-boot-labgit clone --depth=1 \https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git \src/linux-mainline

配置内核

基于x86_64架构的默认配置开始,然后针对QEMU虚拟化环境添加必要的支持选项。这些配置确保内核能在虚拟环境中正常工作并支持我们所需的硬件特性。

先使用默认的配置生成 .config:

SRC=$HOME/linux-boot-lab/src/linux-mainline
OUT=$HOME/linux-boot-lab/build/linux-x86_64make -C "$SRC" O="$OUT" ARCH=x86_64 x86_64_defconfigecho "-boot-lab" > "$OUT/localversion"

-C 表示先 cd 到 $SRC 再执行 make
O=build目录
ARCH=x86_64 指定目标架构
x86_64_defconfig x86 架构下的默认配置文件

因为需要在 Qemu 里面运行,所以需要修改一些配置:

"$SRC/scripts/config" --file "$OUT/.config" \-e DEVTMPFS \-e DEVTMPFS_MOUNT \-e VIRTIO \-e VIRTIO_PCI \-e VIRTIO_BLK \-e VIRTIO_NET \-e EXT4_FS \-e SERIAL_8250 \-e SERIAL_8250_CONSOLEmake -C "$SRC" O="$OUT" ARCH=x86_64 olddefconfig

olddefconfig 作用:读取已有的 .config 对新增配置项自动使用默认值

编译构建

编译内核:

make -C "$SRC" O="$OUT" ARCH=x86_64 -j"$(nproc)" bzImage modules

build 完后可以通过下面命令查看一下版本:

KREL=$(make -s -C "$SRC" O="$OUT" ARCH=x86_64 kernelrelease)
echo "$KREL"

先把编译出来的产物移到 aritifacts 下面:

cd ~/linux-boot-labKART=$HOME/linux-boot-lab/artifacts/kernel/$KREL
MART=$HOME/linux-boot-lab/artifacts/modules/$KRELmkdir -p "$KART" "$MART"cp "$OUT/arch/x86/boot/bzImage" "$KART/vmlinuz-$KREL"
cp "$OUT/.config" "$KART/config-$KREL"
cp "$OUT/System.map" "$KART/System.map-$KREL"

安装 modules:

make -C "$SRC" O="$OUT" ARCH=x86_64 \INSTALL_MOD_PATH="$MART" \modules_install

可以用下面的命令检查一下编译出来的 module 有没有问题,没有问题的话就没有输出:

sudo depmod -b "$MART" "$KREL"

Build the Rootfs

根文件系统(RootFS)是Linux系统运行的基础环境,包含了用户空间的所有必要组件。我们将使用文件系统镜像的方式构建一个最小化的根文件系统。

创建文件系统镜像

创建一个2GB的ext4文件系统镜像作为根文件系统的容器。使用truncate创建稀疏文件以节省磁盘空间:

cd ~/linux-boot-lab/buildrm -f debian-rootfs.img
rm -rf rootfstruncate -s 2G debian-rootfs.img
mkfs.ext4 -F debian-rootfs.img

truncate 本身的作用是调整文件大小,值得注意的是 truncate 创建的是 sparse file,并不会真正写满 2GB;
-s 指定文件大小;
mkfs.ext4 -F 把文件格式化成 ext4 文件系统;
还要一个工具是dd,但是dd会真正占满空间

把这个文件系统挂载到一个目录上:

mkdir -p rootfs
sudo mount -o loop debian-rootfs.img rootfs

安装基础系统

使用 debootstrp 来构建 debian 兼容的最小化用户空间,包含基本的系统组件和包管理器:

sudo debootstrap \--arch=amd64 \--variant=minbase \trixie \rootfs \http://deb.debian.org/debian

包括:

/bin
/etc
/lib
/root
/sbin
/usr
/var

系统配置

为根文件系统配置基本系统信息,包括主机名、网络解析、软件源和系统设置。

修改文件系统里的 host:

sudo tee rootfs/etc/hostname >/dev/null <<EOF
boot-lab
EOFsudo tee rootfs/etc/hosts >/dev/null <<EOF
127.0.0.1 localhost
127.0.1.1 boot-lab
EOF

package source:

sudo tee rootfs/etc/apt/sources.list >/dev/null <<EOF
deb http://deb.debian.org/debian trixie main
deb http://security.debian.org/debian-security trixie-security main
deb http://deb.debian.org/debian trixie-updates main
EOF

resolver:

sudo tee rootfs/etc/resolv.conf >/dev/null <<EOF
nameserver 1.1.1.1
nameserver 8.8.8.8
EOF

locale:

sudo tee rootfs/etc/default/locale >/dev/null <<EOF
LANG=C.UTF-8
EOF

fstab:

sudo tee rootfs/etc/fstab >/dev/null <<EOF
/dev/vda / ext4 defaults 0 1
EOF

fstab的作用:系统启动后,哪些文件系统应该挂载到哪里
这行的意思是:启动后,把 virtio 磁盘 /dev/vda 挂载成系统根目录 /, 文件系统类型是 ext4。

内核集成

将编译好的内核模块和内核文件集成到根文件系统中,确保系统启动时能正确加载内核组件。

拷贝 Linux modules 到 rootfs 里面:

cd ~/linux-boot-labSRC=$HOME/linux-boot-lab/src/linux-mainline
OUT=$HOME/linux-boot-lab/build/linux-x86_64KREL=$(make -s -C "$SRC" O="$OUT" ARCH=x86_64 kernelrelease)
echo "$KREL"sudo mkdir -p build/rootfs/lib/modulessudo rsync -a \"artifacts/modules/$KREL/lib/modules/$KREL" \build/rootfs/lib/modules/

rsync 用于高效同步文件和目录。 rsync 比 cp 更强大,可以:1. 目录同步 2. 保持权限 3. 增量复制 4. 保持 symlink 5. 保持时间戳 6. 大目录树复制。 因为 kernel modules 正好是:大量目录 + symlink + metadata,所以适合 rsync

把 kernel image, config, System.map 拷贝到 rootfs/boot 下面:

sudo mkdir -p build/rootfs/bootsudo cp "artifacts/kernel/$KREL/vmlinuz-$KREL" \"build/rootfs/boot/vmlinuz-$KREL"sudo cp "artifacts/kernel/$KREL/config-$KREL" \"build/rootfs/boot/config-$KREL"sudo cp "artifacts/kernel/$KREL/System.map-$KREL" \"build/rootfs/boot/System.map-$KREL"

系统环境配置

通过chroot进入目标根文件系统,安装必要的系统组件并配置网络和服务。

chroot 前我们需要挂载一些东西到 rootfs 下面:

cd ~/linux-boot-lab/buildsudo mount --bind /dev rootfs/dev
sudo mount --bind /dev/pts rootfs/dev/pts
sudo mount -t proc proc rootfs/proc
sudo mount -t sysfs sysfs rootfs/sys

mount -t 创建并挂载一种文件系统
mount --bind 把已经存在的东西,在另外一个地方再显示一次

chroot:

sudo chroot rootfs /bin/bash

chroot 把当前目录伪装成根目录

进入后就可以开始配置一些环境:

export LANG=C.UTF-8
export LC_ALL=C.UTF-8apt updateapt install -y \systemd-sysv \initramfs-tools \kmod \iproute2 \iputils-ping \openssh-server \ca-certificatescat >/etc/systemd/network/20-wan.network <<EOF
[Match]
Name=en*[Network]
DHCP=yes
EOFsystemctl enable systemd-networkd
systemctl enable ssh
systemctl enable serial-getty@ttyS0.servicemkdir -p /etc/ssh/sshd_config.dcat >/etc/ssh/sshd_config.d/00-boot-lab-root-login.conf <<EOF
PermitRootLogin yes
PasswordAuthentication yes
PermitEmptyPasswords yes
EOFpasswd -d roottouch /etc/securetty
grep -qxF ttyS0 /etc/securetty || echo ttyS0 >> /etc/securetty
grep -qxF console /etc/securetty || echo console >> /etc/securetty

我们的rootfs到现在就build完成了,它包含:

  1. systemd
  2. module tools
  3. initramfs tool
  4. 基本的网络配置
  5. serial login path

Build the Initramfs

文章最开头提到,kernel 启动后需要先加载 initramfs 才是 rootfs。所以我们现在要基于 rootfs 来 build 一个 initramfs。我们仍然是在 chroot 后的环境里面,直接使用 update-initramfs 这个工具来帮我们生成一个 initramfs

KREL=$(ls -1 /lib/modules | sort | tail -1)
echo "$KREL"update-initramfs -c -k "$KREL"ls -l /boot/initrd.img-$KREL

退出 chroot,把我们build好的 initramfs 拷贝出来:

cd ~/linux-boot-labKREL=$(make -s -C "$SRC" O="$OUT" ARCH=x86_64 kernelrelease)sudo cp "build/rootfs/boot/initrd.img-$KREL" \"artifacts/kernel/$KREL/initrd.img-$KREL"

build 完后卸载之前 chroot 前挂载上去的设备:

cd ~/linux-boot-lab/buildsudo umount rootfs/dev/pts
sudo umount rootfs/dev
sudo umount rootfs/proc
sudo umount rootfs/sys
sudo umount rootfs

Booting

使用 QEMU 启动:

cd ~/linux-boot-labSRC=$HOME/linux-boot-lab/src/linux-mainline
OUT=$HOME/linux-boot-lab/build/linux-x86_64KREL=$(make -s -C "$SRC" O="$OUT" ARCH=x86_64 kernelrelease)
echo "$KREL"qemu-system-x86_64 \-m 1024M \-smp 2 \-kernel "artifacts/kernel/$KREL/vmlinuz-$KREL" \-initrd "artifacts/kernel/$KREL/initrd.img-$KREL" \-append "root=/dev/vda rw rootwait console=ttyS0" \-drive file="build/debian-rootfs.img",format=raw,if=virtio \-netdev user,id=net0,hostfwd=tcp::2222-:22 \-device virtio-net-pci,netdev=net0 \-nographic
http://www.jsqmd.com/news/886498/

相关文章:

  • 3大实战秘籍:揭秘raylib如何让游戏开发像搭积木一样简单
  • 2026 上海 GEO 优化机构实力榜:AI 搜索第一推荐位抢占攻略 - GEO优化
  • 智慧养老系统用药管理:精准管控老人用药
  • 2026 广州 GEO 优化机构实力榜:AI 搜索第一推荐位抢占攻略 - GEO优化
  • 用了ChatGPT写论文初稿,如何降低AI率并同步减少文字重复率?
  • CAPL脚本效率翻倍秘诀:巧用testfunction组织你的自动化测试用例
  • LCDC工具包与RoBo6数据集:标准化光曲线分析赋能空间碎片智能识别
  • 当 AI Coding 进入复杂企业系统,为什么提效远没有宣传里那么美好 ?
  • PDF4QT:免费开源的PDF全能工具箱,轻松处理各类文档难题
  • UE5 Niagara实战:用Generate Location Event制作粒子追踪特效(附完整蓝图)
  • OFD转PDF专业解决方案:Ofd2Pdf开源工具全面指南
  • ARM编译器函数性能分析工具链演进与实践
  • 飞书文档一键批量导出:企业知识库迁移效率提升95%的终极解决方案
  • 基于VAE潜在空间与机器学习分类器的恶意软件检测实战
  • UE5增强输入系统如何可靠激活GameplayAbility
  • DeepSeek微服务化部署下的集成测试困局:如何用契约测试+MockLLM在48小时内完成全链路回归?
  • 论文写作效率翻倍?okbiye 毕业论文 AI 功能全解析:从需求到终稿的规范路径
  • 告别混乱绑定!在UE5 GAS中优雅管理技能输入(基于GameplayTag)
  • 渗透测试——漏洞扫描工具
  • 深入拆解 Transformer 注意力机制:从 MHA 到 MLA,大模型性能跃迁的底层密码
  • HEC:基于动态规则生成的MLIR等价性验证工具
  • 真实内网渗透全链路:从OA子系统到域控接管实战
  • 基于Arduino与PID算法DIY高性能SMD焊台:适配Weller RT焊头
  • 告别无效改稿:okbiye 毕业论文写作功能,如何让高校论文从 0 到 1 合规落地
  • 主流模型术数题「翻车」,Tianfu Agent准确率达50%逼近人类Top20选手水平
  • 在Python项目中集成多模型服务实现智能客服问答场景
  • taotoken如何帮助ubuntu开发者应对大模型api的频繁更新与版本迭代
  • GitHub认证升级指南:SSH与PAT双轨实践
  • 通过curl命令快速测试Taotoken API连通性与模型响应基础教程
  • 一文知数据库