ARM64 Linux 6.10内核启动流程2-rootfs 怎么挂上

§1.1 rootfs 怎么挂上

平台:QEMU virt + ARM64。当前命令:

bash 复制代码
qemu-system-aarch64 \
  -M virt \
  -cpu cortex-a57 \
  -m 1024 \
  -kernel linux-6.10/arch/arm64/boot/Image \
  -initrd initramfs.cpio.gz \
  -nographic \
  -append "console=ttyAMA0 nokaslr" \
  -s -S

0. 这条命令和根有关的部分

QEMU 和根的关系
-initrd initramfs.cpio.gz cpio initramfs ,解进已经存在的内存 /。不是 root=/dev/ram,不是 Root_RAM0
-append "console=ttyAMA0 nokaslr" 没有 root= ,saved_root_name 为空
包里的 /init busybox 脚本,最后 exec /bin/sh。有它就不会进 prepare_namespace
sh 复制代码
#!/bin/busybox sh
/bin/busybox --install -s /bin
mount -t proc proc /proc
mount -t sysfs sys /sys
exec /bin/sh

1. 在 main.c 里和挂根相关的挂钩

内核一开始 没有磁盘 /。先有一个空的内存根,再决定要不要切成真根。

复制代码
start_kernel
  vfs_caches_init → mnt_init
       init_rootfs()              // do_mounts.c:tmpfs 还是 ramfs
       init_mount_tree()          // 空的 "/" 挂上          【会走】

kernel_init → kernel_init_freeable
  populate_rootfs(initramfs.c)  // 异步把 cpio 解进 "/"
  wait_for_initramfs()            // 等解压完;不是 do_mounts 入口
  console_on_rootfs()
  if (init_eaccess("/init") != 0)
      prepare_namespace()         // do_mounts.c 主戏      【本套不走】
  kernel_execve("/init" 或 /sbin/init)

wait_for_initramfs() 在 init/initramfs.c,只等 cpio 铺完。

进 do_mounts.c 读挂真根,入口是 prepare_namespace() ,不是 wait_for_initramfs。


2. 核心抽象(读 prepare_namespace 盯这三句)

  1. / 可以换 :启动用空 rootfs 当容器;真根先挂到 /root,再 MS_MOVE + chroot 换成 /。这是挂载命名空间,不是驱动总线。
  2. 根由 cmdline 策略选定 :root= / rootfstype= / ro/rw / rootdelay / rootwait。硬件只是被查表。
  3. 挂根是消费者:块设备由 probe 异步造出;挂之前必须等「这一波 probe」结束。

3. 路 A ------ 你现在:initramfs 铺进空根(不切根)

复制代码
空的内存 /(init_rootfs + init_mount_tree)
    → rootfs_initcall(populate_rootfs)
        unpack_to_rootfs(cpio)          // init/initramfs.c
    → wait_for_initramfs()
    → init_eaccess("/init") 成功
    → 跳过 prepare_namespace / mount_root
    → kernel_execve("/init") → exec /bin/sh

/ 从头到尾就是那块内存盘:先空,再被 initramfs.cpio.gz 填上 busybox。没有第二块盘。

不是 Root_RAM0。 那个是 root=/dev/ram 时 parse_root_device() 返回的老式 ramdisk 块设备。你没有 root=,也进不了 parse_root_device。


4. 路 B ------ BSP 常卡:prepare_namespace 切真根

仅当 没有可 exec 的 /init 时:

c 复制代码
ramdisk_execute_command = NULL;
prepare_namespace();

流水线:

复制代码
rootdelay                         给慢速 USB/MMC 多等几秒
  → wait_for_device_probe()       等已经触发的 probe / deferred / async
  → md_run_setup()
  → parse_root_device(root=)      填 ROOT_DEV;找不到则为 0
  → initrd_load()                 老式块设备 initrd;cpio 路径通常 false
  → rootwait                      ROOT_DEV==0 时轮询设备出现
  → mount_root(saved_root_name)   真根挂到 /root(还不是 /)
  → devtmpfs_mount
  → MS_MOVE + chroot              「/」换身份

mount_root() 按 ROOT_DEV 分发:NFS / CIFS / mtd·ubi / 普通块设备。

块设备:mknod /dev/root → do_mount_root(..., "/root")。

源 = root= 那个设备;目标 = 空 rootfs 里的 /root;fstype = rootfstype= 或逐个试。

之后 MS_MOVE 才把这棵子树变成 /。

你这条命令 没有 root=、有 /init,整段 B 不执行。

以后真板要复现:加盘 + root=/dev/vda,并且让 /init 不存在(或 rdinit= 指到无效路径)。

和 probe 的关系(栅栏,不是本条主模型)

wait_for_device_probe()(drivers/base/dd.c)只保证「已经排队/正在跑的 probe」结束,不管以后还会不会有新设备。

  1. flush_work(deferred_probe_work) --- -EPROBE_DEFER 那一轮
  2. wait_event(probe_waitqueue, probe_count==0) --- PID 1 睡在这里,schedule() 在 wait.h
  3. async_synchronize_full() --- 异步 probe

叫醒:driver_probe_device() 里每次 probe 结束 probe_count-- + wake_up_all。

设备和驱动谁后上总线,谁触发这次 probe(device_add 或 driver_register)。匹配算法属 §1.2。


5. 按你这条命令:会走 / 不走

步骤 走?
init_rootfs / 空 / 走
populate_rootfs 解 cpio 走
wait_for_initramfs 走
prepare_namespace / mount_root / parse_root_device 不走
execve("/init") → exec /bin/sh 走

gdb:断 prepare_namespace / mount_root 不该命中。看根相关断 populate_rootfs、unpack_to_rootfs、kernel_execve。


相关推荐
大哥您好10 小时前
QEMU 11.1.2 多架构编译指南(x86_64 / aarch64 / riscv64)
linux·服务器
Autumn_ing10 小时前
Design.md生成UI怎么验收?举例运营后台的颜色、状态和数据核对
前端·ui·ai·设计系统·paico·ai生成ui·design.md
keyipatience11 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
道满科技11 小时前
Function Calling 参数总出错?只修参数就行
linux·windows·python
朱涛的自习室11 小时前
我给 Gemini 4 和 DeepSeek 搞了套「末位淘汰」,AI 干活会更猛吗?(Day 3)
android·前端·后端
Gauss松鼠会12 小时前
高斯数据库(openGauss)的物理存储结构
运维·服务器·数据库·sql·opengauss·经验总结
Android系统攻城狮12 小时前
Linux Gstreamer深度解析之gst_audio_ring_buffer_prepare_read调用流程与实战(七十四)
linux·运维·服务器·gstreamer音视频·音视频进阶
杨云龙UP12 小时前
PostgreSQL 18.4 数据库整库迁移实战:pg_dump 备份与 pg_restore 恢复完整操作手册
运维·服务器·数据库·postgresql·pg_dump·pg_restore
我科绝伦(Huanhuan Zhou)12 小时前
Linux 通用压力测试脚本合集:磁盘 + 网络
linux·网络·压力测试
土星云SaturnCloud14 小时前
土星云智慧服务区方案:AI视觉+边缘计算+视频存管一体化的全场景智能化落地解析
服务器·人工智能·ai·边缘计算