Linux 内核学习笔记:从零搭建内核开发与调试环境

Linux 内核学习笔记:从零搭建内核开发与调试环境

简介

Linux 内核是操作系统的核心,理解内核的编译、启动和运行机制对于深入理解操作系统原理至关重要。本文以实践为主线,从零开始搭建一套完整的内核学习环境:使用 QEMU 硬件模拟器、编译 Linux 内核、制作根文件系统(initramfs)、通过 BusyBox 构建最小用户空间,最终在 QEMU 中成功启动自定义内核。同时详细解析 /boot 目录结构、initrd/initramfs 的作用与制作方法、GRUB 引导加载器等核心知识。这是一份面向内核学习者的完整实践指南。


一、Linux 内核概述

Linux 内核是操作系统的核心组件,负责管理硬件资源、进程调度、内存管理、文件系统和设备驱动等功能。学习内核开发的第一步,是搭建一个安全、可重复的实验环境,而 QEMU 模拟器就是最理想的选择。

为什么要学习内核?

  • 深入理解操作系统的运行原理
  • 排查系统底层的性能问题和故障
  • 开发内核模块或设备驱动
  • 进行安全研究和系统优化

图片占位符:Linux 内核架构层次图


二、QEMU 硬件模拟器搭建

QEMU(Quick EMUlator)是一个开源的硬件虚拟化模拟器,可以模拟各种硬件平台。使用 QEMU 进行内核开发调试,无需担心损坏真实系统。

2.1 安装 QEMU

以下从源码编译安装 QEMU:

bash 复制代码
# 下载 QEMU 源码
wget https://download.qemu.org/qemu-3.1.1.tar.xz

# 解压并进入目录
tar -xvf qemu-3.1.1.tar.xz
cd qemu-3.1.1

# 配置编译选项(默认配置即可满足基本需求)
./configure

# 编译与安装
make
sudo make install

安装完成后,可以通过以下命令验证安装:

bash 复制代码
qemu-system-x86_64 --version

2.2 QEMU 常用命令速查

命令 说明
qemu-system-x86_64 模拟 x86_64 架构
-kernel bzImage 指定内核镜像文件
-initrd initramfs.cpio.gz 指定初始内存文件系统
-append "cmdline" 传递内核启动参数
-nographic 无图形界面模式
-accel tcg 使用 TCG 软件模拟加速
-m 512M 分配 512MB 内存
-smp 2 使用 2 个 CPU 核心

三、内核编译流程

3.1 下载内核源码

bash 复制代码
# 下载 Linux 内核源码(以 3.10.1 为例)
wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.10.1.tar.xz
tar -xvf linux-3.10.1.tar.xz
cd linux-3.10.1

3.2 配置内核

内核编译前需要进行配置,决定哪些功能编译进内核、哪些编译为模块、哪些不编译。

bash 复制代码
# 方式1:使用默认配置(适合初学者)
make defconfig

# 方式2:使用菜单配置(推荐,可以精确控制每个选项)
make menuconfig

3.3 为 QEMU 配置必要驱动

使用 QEMU 模拟器时,必须选择 Virtio 虚拟化驱动。在 make menuconfig 中配置以下选项:

复制代码
Device Drivers --->
    [*] Virtio drivers --->
        <*> Virtio block driver (EXPERIMENTAL)
        <*> Virtio console driver
        <*> Virtio network driver (EXPERIMENTAL)
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support

Virtio 驱动说明:

  • Virtio block driver:虚拟块设备驱动,用于磁盘 I/O
  • Virtio console driver:虚拟控制台驱动
  • Virtio network driver:虚拟网络驱动
  • initramfs/initrd support:初始内存文件系统支持,内核启动必须

3.4 编译内核

bash 复制代码
# 编译内核(-j 参数指定并行编译线程数,加速编译)
make -j32

# 编译完成后,内核镜像生成在:
# arch/x86/boot/bzImage

编译产物说明:

  • bzImage:压缩的内核可执行镜像(Big zImage),这是 QEMU 启动需要的文件
  • vmlinux:未压缩的内核可执行文件(包含调试信息,用于调试)

图片占位符:内核编译配置菜单截图

3.5 编译内核模块(可选)

bash 复制代码
# 编译内核模块
make modules

# 安装内核模块到系统
sudo make modules_install

四、制作根文件系统(BusyBox + initramfs)

内核启动后需要一个根文件系统来运行用户空间的程序。最简方案是使用 BusyBox 构建一个 initramfs(初始内存文件系统)。

4.1 下载并编译 BusyBox

BusyBox 是一个集成了大量常用 Linux 命令的精简工具集,被称为"嵌入式 Linux 的瑞士军刀"。

bash 复制代码
# 下载 BusyBox
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xvf busybox-1.36.1.tar.bz2
cd busybox-1.36.1

# 配置 BusyBox
make menuconfig

4.2 配置 BusyBox 为静态编译

这一步非常重要!静态编译意味着 BusyBox 不依赖任何外部动态库,可以独立运行在 initramfs 中:

复制代码
Settings --->
    [*] Build static binary (no shared libs)
bash 复制代码
# 编译 BusyBox
make

# 安装 BusyBox 到 _install 目录
make install

安装完成后会在当前目录生成 _install 目录,其中包含 BusyBox 编译出的所有工具(ls、cat、sh、mount 等)。

4.3 构建 initramfs 目录结构

bash 复制代码
cd _install

# 创建必要的系统目录
mkdir -p dev proc sys etc init

# 创建必要的设备节点
sudo mknod dev/console c 5 1
sudo mknod dev/null c 1 3
sudo mknod dev/tty c 5 0

4.4 编写 init 脚本

init 脚本是内核启动后执行的第一个用户空间程序,PID 为 1:

bash 复制代码
cat > init << 'EOF'
#!/bin/sh

echo "======================================"
echo "  Hello from the initramfs!"
echo "  Welcome to custom Linux kernel"
echo "======================================"

# 挂载内核虚拟文件系统
mount -t proc none /proc       # 进程信息文件系统
mount -t sysfs none /sys       # 系统设备信息文件系统
mount -t devtmpfs none /dev    # 设备文件系统

# 打印系统信息
echo "Kernel version:"
cat /proc/version

echo ""
echo "Available memory:"
cat /proc/meminfo | head -3

# 启动一个交互式 shell
echo ""
echo "Starting shell..."
exec /bin/sh
EOF

# 给 init 脚本添加执行权限
chmod +x init

init 脚本解析:

  • mount -t proc none /proc:挂载 proc 文件系统,提供进程和内核信息
  • mount -t sysfs none /sys:挂载 sysfs 文件系统,提供设备驱动信息
  • mount -t devtmpfs none /dev:挂载 devtmpfs,自动创建设备节点
  • exec /bin/sh:使用 exec 替换当前进程为 shell,确保 shell 的 PID 为 1

4.5 打包为 cpio 镜像

bash 复制代码
# 在 _install 目录下执行
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz

这条命令的含义:

  • find . -print0:列出当前目录下所有文件,用 null 字符分隔(处理含空格的文件名)
  • cpio --null -ov --format=newc:将文件打包为 newc 格式的 cpio 归档
  • gzip -9:使用最大压缩率进行 gzip 压缩

生成的 initramfs.cpio.gz 就是一个完整的、可以被内核直接加载的根文件系统镜像。


五、使用 QEMU 启动自定义内核

5.1 启动命令

bash 复制代码
qemu-system-x86_64 \
    -kernel ./linux-3.10.1/arch/x86/boot/bzImage \
    -initrd ./initramfs.cpio.gz \
    -append "console=ttyS0 earlyprintk=ttyS0,115200 init=/init nokaslr rdinit=/init debug" \
    -nographic \
    -accel tcg

5.2 启动参数详解

参数 说明
-kernel bzImage 指定编译好的内核镜像
-initrd initramfs.cpio.gz 指定 initramfs 镜像
console=ttyS0 将控制台输出重定向到串口(配合 -nographic)
earlyprintk=ttyS0,115200 启用早期内核打印,用于调试启动过程
init=/init 指定内核启动后执行的第一个用户程序
nokaslr 禁用内核地址空间随机化(方便调试)
rdinit=/init 指定 initramfs 中的 init 程序路径
debug 启用内核调试信息输出
-nographic 无图形界面,使用串口终端
-accel tcg 使用 TCG 纯软件模拟(兼容性最好)

5.3 启动成功的标志

如果一切配置正确,你将看到类似如下的输出:

复制代码
...
Hello from the initramfs!
Welcome to custom Linux kernel
======================================
Kernel version:
Linux version 3.10.1 ...

Available memory:
MemTotal:        512000 kB
MemFree:         480000 kB

Starting shell...
/ #

看到 / # 提示符,说明内核启动成功,BusyBox shell 已经运行。

图片占位符:QEMU 成功启动内核的终端截图


六、/boot 目录结构详解

在真实的 Linux 系统中,/boot 目录存放着系统启动所需的所有文件。理解这些文件的含义对内核学习非常重要。

6.1 目录结构总览

复制代码
/boot/
├── vmlinuz-3.10.1              # 压缩的内核镜像
├── initramfs-3.10.1.img        # 初始内存文件系统
├── initrd.img-3.10.1           # 初始内存磁盘(旧格式)
├── System.map-3.10.1           # 内核符号映射表
├── config-3.10.1               # 内核编译配置
├── grub/                       # GRUB 引导加载器目录
│   └── grub.cfg                # GRUB 主配置文件
└── efi/                        # UEFI 启动目录
    └── EFI/

6.2 核心文件解析

vmlinuz(压缩的内核镜像)

vmlinuz-<版本号> 是压缩的可引导 Linux 内核镜像,通常是 bzImage 格式。这个名字来源于早期的 Unix 系统:"vm" 表示虚拟内存(Virtual Memory),"z" 表示压缩(gzip 压缩)。

bash 复制代码
# 查看当前系统使用的内核版本
uname -r
# 输出类似:5.15.0-generic

# 对应的内核镜像文件
ls -lh /boot/vmlinuz-*
initrd.img / initramfs(初始内存文件系统)

initrd.img-<版本号>initramfs-<版本号> 是初始内存磁盘/文件系统,包含启动初期需要的模块和工具。后面会详细讲解。

System.map(内核符号映射表)

System.map-<版本号> 是内核符号映射表,记录了内核中函数和变量的内存地址映射,主要用于调试和内核错误解析(如 oops 消息中的地址翻译)。

bash 复制代码
# 查看 System.map 的内容(前几行)
head -5 /boot/System.map-$(uname -r)
# 输出示例:
# ffffffff81000000 T _text
# ffffffff81000000 T startup_64
# ffffffff81001000 T do_one_initcall
GRUB 引导加载器配置
bash 复制代码
/boot/grub/grub.cfg    # GRUB 主配置文件(一般不要手动编辑)
/boot/efi/EFI/         # UEFI 系统的启动文件

更新 GRUB 配置的正确方式:

bash 复制代码
# Debian/Ubuntu
sudo update-grub

# CentOS/RHEL
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

七、initramfs 与 initrd 的作用与制作

7.1 为什么需要 initramfs?

initrd(Initial RAM Disk)是一个临时的根文件系统,在内核启动后、真正的根文件系统挂载之前使用。它的核心作用是:

  1. 提供内核模块:包含挂载真实根文件系统所需的驱动(如 SCSI、RAID、LVM、加密等)。

  2. 执行初始化脚本:运行必要的初始化操作。

  3. 加载关键工具:提供基础工具来探测和挂载真正的根文件系统。

  4. 处理特殊配置:如磁盘加密、网络根文件系统等。

    启动流程:
    BIOS/UEFI → GRUB → 加载 vmlinuz → 加载 initramfs →
    执行 init 脚本 → 加载驱动 → 挂载真实根文件系统 →
    切换到真实根文件系统 → 启动 systemd/init → 进入系统

图片占位符:Linux 系统启动流程图

7.2 使用发行版工具制作 initramfs

Ubuntu/Debian:mkinitramfs
bash 复制代码
# 1. 确保内核模块已安装
sudo make modules_install

# 2. 制作 initramfs(最简单方式)
sudo mkinitramfs -o /boot/initrd.img-3.10.1-custom 3.10.1-custom

# 3. 高级选项:包含特定模块
sudo mkinitramfs -o /boot/initrd.img-3.10.1-custom 3.10.1-custom \
    --modules "ext4 vfat nls_utf8 virtio"

# 4. 查看生成的内容
lsinitramfs /boot/initrd.img-3.10.1-custom | head -20
CentOS/RHEL/Fedora:dracut
bash 复制代码
# 1. 制作 initramfs
sudo dracut --force /boot/initramfs-3.10.1-custom.img 3.10.1-custom

# 2. 包含额外驱动
sudo dracut --force --add-drivers "virtio ext4" \
    /boot/initramfs-3.10.1-custom.img

# 3. 显示详细制作过程
sudo dracut --force -v /boot/initramfs-3.10.1-custom.img 3.10.1-custom

7.3 手动制作 initramfs

手动制作 initramfs 的过程正是我们在第四章中所做的,核心步骤回顾:

bash 复制代码
# 1. 使用 BusyBox 构建基础文件系统
cd busybox-1.36.1
make menuconfig  # 配置静态编译
make && make install

# 2. 完善目录结构
cd _install
mkdir -p dev proc sys etc init

# 3. 编写 init 脚本
cat > init << 'EOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
exec /bin/sh
EOF
chmod +x init

# 4. 打包为 cpio 镜像
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz

7.4 initramfs vs initrd 对比

特性 initrd(旧) initramfs(新)
格式 文件系统镜像(ext2等) cpio 归档
挂载方式 作为块设备挂载 解压到内存(tmpfs)
大小 固定大小 动态增长
灵活性 较低
内核配置 CONFIG_BLK_DEV_INITRD CONFIG_BLK_DEV_INITRD
推荐程度 旧系统 现代系统推荐

八、GRUB 引导加载器

GRUB(GRand Unified Bootloader)是 Linux 系统中最常用的引导加载器,负责在计算机启动时加载操作系统内核。

8.1 GRUB 的工作流程

复制代码
BIOS/UEFI → MBR/GPT → GRUB Stage 1 → GRUB Stage 2 →
显示启动菜单 → 加载 vmlinuz 和 initramfs → 启动内核

8.2 GRUB 配置示例

bash 复制代码
# /boot/grub/grub.cfg 中的典型条目
menuentry 'Linux 3.10.1-custom' {
    set root='hd0,msdos1'
    linux /boot/vmlinuz-3.10.1-custom root=/dev/sda1 ro quiet
    initrd /boot/initramfs-3.10.1-custom.img
}

各字段含义:

  • set root:指定 /boot 所在的磁盘分区
  • linux:指定内核镜像路径及启动参数
  • initrd:指定 initramfs 镜像路径
  • root=/dev/sda1:真实根文件系统所在分区
  • ro:以只读方式挂载根文件系统
  • quiet:减少启动信息输出

8.3 更新 GRUB 配置

bash 复制代码
# Debian/Ubuntu
sudo update-grub

# CentOS/RHEL/Fedora
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

# 安装新内核后通常需要更新 GRUB
sudo make install   # 编译安装内核,会自动调用 grub-install

九、内核模块管理

内核模块(Kernel Module)是可以动态加载和卸载的内核代码,无需重新编译整个内核。

9.1 常用模块管理命令

bash 复制代码
# 查看已加载的模块
lsmod

# 加载模块
sudo insmod my_module.ko

# 卸载模块
sudo rmmod my_module

# 自动处理模块依赖关系
sudo modprobe virtio_net    # 加载(自动处理依赖)
sudo modprobe -r virtio_net # 卸载

# 查看模块信息
modinfo virtio_net

9.2 Virtio 驱动模块

在 QEMU 环境中,Virtio 驱动是性能最优的虚拟化设备驱动方案。相比于传统的硬件模拟(如 e1000 网卡),Virtio 提供了更高的 I/O 性能。

Virtio 驱动 用途 配置选项
virtio_blk 虚拟块设备(磁盘) CONFIG_VIRTIO_BLK
virtio_net 虚拟网络设备 CONFIG_VIRTIO_NET
virtio_console 虚拟控制台 CONFIG_VIRTIO_CONSOLE
virtio_pci PCI 传输层 CONFIG_VIRTIO_PCI
virtio_balloon 内存气球(动态内存) CONFIG_VIRTIO_BALLOON

十、完整实践流程回顾

将前面所有步骤串联起来,形成一套完整的内核学习实践流程:

bash 复制代码
# ===== 第一步:安装 QEMU =====
wget https://download.qemu.org/qemu-3.1.1.tar.xz
tar -xvf qemu-3.1.1.tar.xz && cd qemu-3.1.1
./configure && make && sudo make install

# ===== 第二步:编译内核 =====
wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.10.1.tar.xz
tar -xvf linux-3.10.1.tar.xz && cd linux-3.10.1
make defconfig
make menuconfig   # 选择 Virtio 驱动
make -j32

# ===== 第三步:制作根文件系统 =====
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xvf busybox-1.36.1.tar.bz2 && cd busybox-1.36.1
make menuconfig   # 选择静态编译
make && make install
cd _install
mkdir -p dev proc sys etc
# 编写 init 脚本...
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz

# ===== 第四步:启动! =====
qemu-system-x86_64 \
    -kernel ./linux-3.10.1/arch/x86/boot/bzImage \
    -initrd ./initramfs.cpio.gz \
    -append "console=ttyS0 earlyprintk=ttyS0,115200 init=/init nokaslr rdinit=/init debug" \
    -nographic -accel tcg

总结

本文从零搭建了一套完整的 Linux 内核学习环境,涵盖了以下核心内容:

  1. QEMU 模拟器:提供了安全、可重复的内核调试环境,无需真实硬件。
  2. 内核编译 :从 make defconfigmake menuconfig 配置 Virtio 驱动,最终生成 bzImage。
  3. 根文件系统:通过 BusyBox 静态编译 + init 脚本 + cpio 打包,构建最小可用的用户空间。
  4. 启动流程:理解从 GRUB → vmlinuz → initramfs → init 的完整启动链路。
  5. /boot 目录:掌握 vmlinuz、initrd、System.map、grub.cfg 等关键文件的作用。
  6. initramfs 制作:学会使用 mkinitramfs、dracut 或手动方式制作初始内存文件系统。
  7. 内核模块:了解 Virtio 驱动等关键模块的配置和使用。

内核学习是一个循序渐进的过程,建议在掌握本文内容后,进一步学习内核模块编程、设备驱动开发、内核调试等进阶主题。


原始笔记来源:

  • E:/Work/Notes/zyh/Liunx内核学习笔记.md -- QEMU模拟器、内核编译、根文件系统initramfs、BusyBox、/boot目录、GRUB、vmlinuz、initrd.img、mkinitramfs/dracut
相关推荐
GottdesKrieges1 小时前
OceanBase备份常见问题
linux·网络·oceanbase
白菜欣2 小时前
Linux —进程概念
linux·运维·服务器
iuu_star2 小时前
Vue+FastAPI 项目宝塔Linux部署指南
linux·运维·fastapi
楼田莉子2 小时前
仿Muduo的高并发服务器:Channel模块与Poller模块
linux·服务器·c++·学习·设计模式
zhouwy1132 小时前
Linux网络编程从入门到精通
linux·c++
zhangrelay2 小时前
ROS Kinetic-信号与系统-趣味案例
linux·笔记·学习·ubuntu
IMPYLH2 小时前
Linux 的 tail 命令
linux·运维·服务器·bash
weixin_446260852 小时前
应用实战篇:利用 DeepSeek V4 构建生产级 AI 应用的全流程与最佳实践
大数据·linux·人工智能
Nightwish52 小时前
Linux随记(三十)
linux·运维·mysql·ambari