使用QEMU启动自编译Linux内核并挂载ext4实验盘

使用QEMU启动自编译Linux内核并挂载ext4实验盘

本文记录一套操作流程:使用~/workspace/Linux/linux里编出来的bzImage,通过QEMU启动一个最小initramfs环境,并以virtio块设备的方式外挂一块ext4格式的虚拟磁盘镜像,便于在虚拟机内进行ext4挂载、卸载与ftrace跟踪等操作。

环境约定(本仓库实际路径)

  • 内核源码目录:~/workspace/Linux/linux
  • 实验目录:~/workspace/Linux/qemu-lab
  • 内核镜像:~/workspace/Linux/linux/arch/x86/boot/bzImage

1. 编译内核

如果编译时遇到以下两类常见阻塞,可以用对应方式处理:

1.1 gendwarfksyms缺host依赖(dwarf.h)

报错示例:fatal error: dwarf.h: 没有那个文件或目录

两种解决方式(二选一):

  • 安装依赖(推荐长期方案):安装 libdw-dev
  • 或禁用该特性(快速通过编译):
bash 复制代码
cd ~/workspace/Linux/linux
./scripts/config --disable GENDWARFKSYMS
make olddefconfig

1.2 缺debian/canonical-certs.pem

报错示例:没有规则可制作目标 debian/canonical-certs.pem

处理方式(置空trusted/revocation keys):

bash 复制代码
cd ~/workspace/Linux/linux
./scripts/config --set-str SYSTEM_TRUSTED_KEYS ""
./scripts/config --set-str SYSTEM_REVOCATION_KEYS ""
make olddefconfig

1.3 编译

bash 复制代码
cd ~/workspace/Linux/linux
make -j"$(nproc)"

主要产物:

  • vmlinux
  • arch/x86/boot/bzImage

2. 安装宿主机工具

bash 复制代码
sudo apt install qemu-system-x86 e2fsprogs busybox-static cpio gzip

说明:

  • qemu-system-x86是Ubuntu/Debian上的软件包名,安装后会提供qemu-system-x86_64命令用于启动内核
  • busybox-static用于制作最小initramfs(避免动态库依赖)
  • e2fsprogs提供mkfs.ext4

3. 准备实验目录

bash 复制代码
mkdir -p ~/workspace/Linux/qemu-lab/initramfs/{bin,sbin,proc,sys,dev,mnt}

4. 制作最小initramfs(关键点:先安装busybox applets)

把busybox放进initramfs:

bash 复制代码
cp -a /usr/bin/busybox ~/workspace/Linux/qemu-lab/initramfs/bin/busybox
ln -sf busybox ~/workspace/Linux/qemu-lab/initramfs/bin/sh

创建 /init

~/workspace/Linux/qemu-lab/initramfs/init

推荐用一条命令直接生成该文件(避免手动复制粘贴漏行):

bash 复制代码
cat > ~/workspace/Linux/qemu-lab/initramfs/init <<'EOF'
#!/bin/sh
mkdir -p /proc /sys /dev /mnt /bin /sbin
/bin/busybox --install -s /bin
/bin/busybox --install -s /sbin
mount -t proc proc /proc
mount -t sysfs sys /sys
mount -t devtmpfs dev /dev
mkdir -p /sys/kernel/tracing
mount -t tracefs nodev /sys/kernel/tracing
mkdir -p /sys/kernel/debug
mount -t debugfs nodev /sys/kernel/debug
echo "initramfs: mount ext4 /dev/vda -> /mnt"
mount -t ext4 /dev/vda /mnt
echo "initramfs: mount exit=$?"
ls -al /dev/vda 2>/dev/null || true
ls -al /mnt 2>/dev/null || true
exec /bin/sh
EOF
chmod +x ~/workspace/Linux/qemu-lab/initramfs/init

注意:

  • 如果没有执行 busybox --install -s,会出现 mount: not found(因为只有 busybox 本体,没有 mount 这个命令名的链接)
  • 上面这份/init默认会在进入shell前自动尝试挂载ext4实验盘(virtio-blk的/dev/vda),便于确认环境是否正常

打包initramfs:

bash 复制代码
chmod +x ~/workspace/Linux/qemu-lab/initramfs/init
cd ~/workspace/Linux/qemu-lab/initramfs
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ~/workspace/Linux/qemu-lab/initramfs.cpio.gz

产物:

  • ~/workspace/Linux/qemu-lab/initramfs.cpio.gz

5. 创建ext4实验盘镜像

bash 复制代码
dd if=/dev/zero of=~/workspace/Linux/qemu-lab/ext4.img bs=1M count=1024 status=none
mkfs.ext4 -F ~/workspace/Linux/qemu-lab/ext4.img

产物:

  • ~/workspace/Linux/qemu-lab/ext4.img(1GiB,ext4)

6. QEMU启动

bash 复制代码
cd ~/workspace/Linux/linux

qemu-system-x86_64 \
  -m 1024 \
  -smp 2 \
  -kernel arch/x86/boot/bzImage \
  -initrd ~/workspace/Linux/qemu-lab/initramfs.cpio.gz \
  -drive file=~/workspace/Linux/qemu-lab/ext4.img,if=virtio,format=raw \
  -append "console=ttyS0 console=ttyS1 rdinit=/init" \
  -nographic

-initrd用于向内核提供initramfs(初始内存根文件系统)。本实验使用rdinit=/init,因此内核启动后会执行initramfs里的/init脚本进入BusyBox环境。若不提供-initrd,就需要改成"磁盘rootfs"方案(镜像里要有/sbin/init),或把initramfs直接编进内核。 这里的"virtio-blk"指的是磁盘在QEMU里通过virtio的块设备模型提供给虚拟机(if=virtio),在虚拟机里通常体现为/dev/vda。除了virtio-blk,也可以用virtio-scsi(通常是/dev/sda)、IDE/SATA(AHCI)、NVMe等方式挂载同一个镜像;对学习ext4来说,virtio-blk/virtio-scsi都很常用。


7. 在虚拟机内挂载ext4并验证

在QEMU里:

sh 复制代码
mkdir -p /mnt
mount -t ext4 /dev/vda /mnt
ls -al /mnt

预期看到 lost+found 等目录;内核日志中会出现类似:

  • virtio_blk ... [vda] ...
  • EXT4-fs (vda): mounted filesystem ... r/w

8. 手动退出QEMU

如果是-nographic模式想手动退出QEMU(不关机),可在QEMU窗口按:

  • Ctrl-a x(退出)
  • Ctrl-a c进入QEMU monitor,再输入quit

9. ftrace:函数调用跟踪(tracefs)

ftrace是Linux内核自带的轻量级跟踪框架,可以在不改代码/不重编译的情况下动态开启跟踪,用于观察内核函数调用、调用耗时与部分调用关系。对学习文件系统来说,它非常适合用来抓取某次mount/umount/open/read/write触发的关键路径(如ext4_fill_super__ext4_igetext4_map_blocks等),从而把"现象"映射到"具体走了哪些内核函数"。

9.1 /sys/kernel/tracing可能是空的

/sys/kernel/tracingtracefs的挂载点。如果只挂了proc/sysfs/devtmpfs,但没有挂载tracefs,这个目录会是空的,自然也就看不到current_tracer/trace/trace_pipe/set_ftrace_filter等接口文件。

本实验的 /init 已经加入了:

sh 复制代码
mkdir -p /sys/kernel/tracing
mount -t tracefs nodev /sys/kernel/tracing

9.2 进入虚拟机后手动确认

sh 复制代码
mount | grep -E 'tracefs|debugfs' || true
ls /sys/kernel/tracing | head

如果还没挂载,可以手动挂:

sh 复制代码
mount -t tracefs nodev /sys/kernel/tracing

9.3 function tracer示例

sh 复制代码
cd /sys/kernel/tracing

# 1) 清理
echo nop > current_tracer
echo 0 > tracing_on
: > trace

# 2) 设过滤函数
echo "ext4_*" > set_ftrace_filter
echo function > current_tracer
echo 1 > tracing_on

# 3) 强制触发(最稳:重新挂载)
umount /mnt
mount -t ext4 /dev/vda /mnt

# 4) 关tracing并看结果
echo 0 > tracing_on
cat trace    #能看到umount/mount的函数调用流程

说明:

  • 第1步把tracer设为nop并清空trace,避免混入上一次实验的残留
  • 第2步用set_ftrace_filter限定只记录ext4_*相关函数
  • 第3步选择umount/mount是因为它们会稳定触发ext4的卸载/挂载路径(如ext4_kill_sbext4_put_superext4_fill_super__ext4_iget等),比单纯ls更容易看到关键流程
  • 第4步关闭tracing_on后再查看trace,能得到一个完整的"操作->触发的函数链"快照;可配合grep ext4_tail -n 200聚焦阅读

示例输出(节选自~/workspace/Linux/qemu-lab/trace.log,用于参考格式与字段含义):

text 复制代码
# tracer: function
# entries-in-buffer/entries-written: 5850/5850   #P:2
#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
          umount-99      [000] .....    75.535497: ext4_kill_sb <-deactivate_locked_super
          umount-99      [000] .....    75.535971: ext4_sync_fs <-sync_filesystem
           mount-100     [000] .....    84.846012: ext4_init_fs_context <-alloc_fs_context
           mount-100     [000] .....    84.846329: ext4_fill_super <-get_tree_bdev_flags
           mount-100     [000] .....    84.848419: ext4_inode_csum <-__ext4_iget

字段说明:

  • TASK-PID:触发本次调用的进程(这里的umount-99mount-100对应第3步的卸载/挂载)
  • CPU#:事件发生在哪个CPU上(如[000];多核时会交错出现)
  • TIMESTAMP:从启动开始的时间戳(秒)
  • FUNCTION<-CALLERFUNCTION被调用,直接调用者是CALLER。例如ext4_kill_sb<-deactivate_locked_super表示VFS在卸载super时调用了ext4的.kill_sb回调

相关推荐
JP-Destiny9 小时前
Linux-配置Ubuntu的IP
linux·tcp/ip·ubuntu
ofoxcoding9 小时前
Codex 官网访问 + 完整安装教程:macOS / Windows / Linux 一次跑通(2026)
linux·windows·macos·ai
magic_now9 小时前
systemctl stop 会杀死子进程吗?
linux
sulikey9 小时前
如何在Ubuntu中判断是否已安装ncurses库
linux·运维·ubuntu·ncurses
Cat_Rocky9 小时前
Linux学习-ansible自动化
linux·学习·ansible
programhelp_9 小时前
Ramp OA 四关全过,CodeSignal OOD 完整复盘
linux·前端·python
_Emma_9 小时前
【Linux网络】Linux网络协议栈问题汇集
linux·网络·网络协议
minji...9 小时前
Linux 网络基础之数据链路层(十三)认识以太网,认识MAC地址和MTU,局域网(以太网)通信原理
linux·网络·以太网·交换机·数据链路层·mac地址·局域网通信
minji...9 小时前
Linux 网络基础之数据链路层(十四)ARP协议及原理,ARP欺骗
linux·网络·智能路由器·ip·arp协议·arp欺骗
小猫咪019 小时前
Linux 文件权限详解:chmod、chown、umask 到底怎么用?
linux