4. 内核挂载根文件系统的关键函数:prepare_namespace、mount_root 与 init/do_mounts.c
根文件系统的挂载是 Linux 内核启动过程中从“内核初始化”过渡到“用户空间”的临界点。一旦内核完成硬件探测、驱动加载和设备初始化,便进入 prepare_namespace() 函数,该函数负责解析启动参数、识别根设备、调用具体的文件系统驱动,最终将根文件系统挂载到 / 目录。整个流程的核心代码集中在 init/do_mounts.c 文件中。
关键认知: 根文件系统挂载失败是内核启动失败最常见的原因之一。理解
prepare_namespace → mount_root 的执行路径,是定位“Kernel panic - not syncing: VFS: Unable to mount root fs”等致命错误的必备技能。
4.1 函数调用链与执行流程
从 start_kernel() 到根文件系统就绪,关键函数调用链如下:
start_kernel()→ 完成架构初始化、内存管理、调度器、中断等子系统初始化。rest_init()→ 创建kernel_init内核线程。kernel_init()→ 调用prepare_namespace()。prepare_namespace()→ 解析root=参数,处理initrd/initramfs,最终调用mount_root()。mount_root()→ 打开根设备,调用get_sb_bdev()或mount_bdev()挂载文件系统。kernel_init()→ 挂载成功后,切换到根目录并执行/sbin/init或指定的init=程序。
调试入口: 在
kernel_init() 函数中插入 printk 或使用 early_printk,可以实时观察根设备号、文件系统类型和挂载返回值,是排查挂载失败的第一道防线。
4.2 prepare_namespace() 函数深度解析
prepare_namespace() 定义在 init/do_mounts.c 中,是根文件系统挂载的“总调度器”。其执行逻辑可分为以下阶段:
| 执行阶段 | 关键操作 | 涉及变量/函数 | 失败后果 |
|---|---|---|---|
| 1. 参数解析 | 读取 root= 启动参数,确定根设备 |
root_dev_setup(), ROOT_DEV |
设备号无效,挂载失败 |
| 2. initrd 处理 | 如果指定了 initrd=,加载并挂载临时根文件系统 |
initrd_load() |
initrd 损坏或格式错误导致内核 panic |
| 3. 设备等待 | 等待根设备在 /dev 中出现(异步探测场景) |
wait_for_device_probe() |
设备未就绪,超时后挂载失败 |
| 4. 文件系统识别 | 根据 rootfstype= 或自动探测确定文件系统类型 |
get_fs_type() |
不支持的文件系统类型导致挂载失败 |
| 5. 调用 mount_root | 最终调用 mount_root() 执行实际挂载 |
mount_root() |
挂载失败,进入 kernel panic |
常见陷阱: 在嵌入式系统中,根设备可能是
/dev/mmcblk0p2 或 /dev/sda1,但内核在 prepare_namespace() 阶段可能尚未完成对应驱动注册。此时需要检查驱动加载顺序,或使用 rootwait 参数让内核等待设备就绪。
4.3 mount_root() 函数与挂载机制
mount_root() 是实际执行文件系统挂载的函数。其核心流程如下:
- 打开根设备: 通过
blkdev_get_by_dev()获取块设备句柄。 - 获取文件系统类型: 调用
get_fs_type()根据rootfstype=参数或自动探测结果,找到对应的文件系统驱动(如ext4、squashfs、nfs)。 - 调用挂载接口: 执行
mount_bdev()或get_sb_bdev(),将文件系统挂载到/目录。 - 错误处理: 如果挂载失败,打印错误信息并进入
panic()。
// init/do_mounts.c 中的 mount_root() 简化逻辑
void __init mount_root(void)
{
struct file_system_type *fs_type;
struct block_device *bdev;
int err;
// 1. 打开根设备
bdev = blkdev_get_by_dev(ROOT_DEV, FMODE_READ | FMODE_WRITE, NULL);
if (IS_ERR(bdev)) {
printk("VFS: Cannot open root device \"%s\"\n", saved_root_name);
panic("VFS: Unable to mount root fs on %s", saved_root_name);
}
// 2. 获取文件系统类型
fs_type = get_fs_type(root_fs_names);
if (!fs_type) {
printk("VFS: Cannot find filesystem type \"%s\"\n", root_fs_names);
panic("VFS: Unable to mount root fs");
}
// 3. 执行挂载
err = mount_bdev(fs_type, 0, bdev, NULL);
if (err) {
printk("VFS: Cannot mount root fs on %s\n", saved_root_name);
panic("VFS: Unable to mount root fs");
}
}
调试技巧: 在
mount_root() 中,saved_root_name 保存了 root= 参数的原始字符串。如果挂载失败,内核会打印该字符串,帮助确认根设备参数是否正确传递。
4.4 init/do_mounts.c 文件结构概览
init/do_mounts.c 是根文件系统挂载的核心实现文件,包含以下主要函数和全局变量:
| 函数/变量 | 作用 | 关键依赖 |
|---|---|---|
prepare_namespace() |
根文件系统挂载的总入口 | root_dev_setup(), initrd_load() |
mount_root() |
执行实际挂载操作 | blkdev_get_by_dev(), mount_bdev() |
mount_block_root() |
处理块设备根文件系统的挂载 | get_fs_type() |
initrd_load() |
加载并挂载 initrd | rd_load_image(), rd_load_prompt() |
ROOT_DEV |
全局变量,存储根设备号 | 由 root_dev_setup() 设置 |
saved_root_name |
保存 root= 参数的字符串 |
用于调试输出 |
root_fs_names |
保存 rootfstype= 参数的字符串 |
用于指定文件系统类型 |
4.5 常见挂载失败场景与根因定位
基于 prepare_namespace() 和 mount_root() 的执行逻辑,以下表格总结了最常见的根文件系统挂载失败场景及其根因:
| 失败现象 | 内核日志关键信息 | 根因分析 | 修复方向 |
|---|---|---|---|
| VFS: Cannot open root device | VFS: Cannot open root device "mmcblk0p2" |
根设备驱动未加载或设备不存在 | 检查驱动编译选项、设备树配置、rootwait 参数 |
| VFS: Cannot find filesystem type | VFS: Cannot find filesystem type "ext4" |
文件系统驱动未编译进内核 | 将文件系统驱动编译为 built-in(非模块) |
| VFS: Cannot mount root fs | VFS: Cannot mount root fs on "sda1" |
文件系统损坏或格式不匹配 | 检查文件系统完整性、rootfstype= 参数 |
| Kernel panic - not syncing | No filesystem could mount root |
所有尝试的文件系统类型均挂载失败 | 检查内核配置、initramfs 内容、根设备分区表 |
| initramfs 解压失败 | Unpacking initramfs failed |
initramfs 镜像损坏或格式错误 | 重新生成 initramfs,检查压缩格式 |
实战建议: 在嵌入式开发中,建议在
prepare_namespace() 之前添加 printk 打印 ROOT_DEV 和 saved_root_name,确认内核接收到的根设备参数与预期一致。这是最快速定位“参数传递错误”类问题的方法。
4.6 本章小结
prepare_namespace()是根文件系统挂载的总入口,负责参数解析、initrd 处理和最终挂载调度。mount_root()执行实际的块设备打开、文件系统类型获取和挂载操作。init/do_mounts.c集中了根文件系统挂载的所有核心逻辑,是内核启动调试的关键文件。- 挂载失败时,内核日志中的
VFS:错误信息直接指向问题根因,是定位故障的第一手资料。 - 理解这三个关键函数,等于掌握了根文件系统挂载的“命脉”,是分析和修复启动失败问题的必备知识。