本文约3400字,在【深度剖析】: Linux ARM64 架构下ramdisk:集成、引导与内核联动机制这篇帖子中理解了ramdisk的工作机制,这一篇继续整理从ramdisk切换到根文件系统的详细流程。
关注公众号, 即可获得与Linux相关的电子书籍以及常用开发工具,文末有文档清单。
在嵌入式Linux系统启动中,ramdisk(临时根文件系统)的核心作用是“过渡支撑”——由于启动初期内核无法直接识别硬盘、Flash等存储设备(需加载对应驱动),先将ramdisk加载到内存并作为临时根文件系统启动,待驱动就绪、目标根文件系统可访问后,再切换到永久根文件系统(如ext4、ubifs)。整个过程需内核与用户态程序协同,分为6大核心步骤,全程保障系统不中断。
一 切换前提条件
切换前需满足3个核心条件,否则会导致切换失败或系统崩溃:
>>ramdisk已正常挂载并启动:系统当前运行在ramdisk中,init进程(或systemd)正常执行,具备基本命令行工具(mount、insmod、pivot_root/switch_root等)。
>>目标根文件系统可访问:存储设备(硬盘、eMMC、NAND Flash)已被内核识别(对应设备节点如/dev/mmcblk0p2存在),且根文件系统镜像完整(无损坏、格式正确)。
>>驱动依赖已就绪:加载目标存储设备的驱动(如SD卡驱动、SATA驱动、Flash控制器驱动),以及根文件系统格式对应的驱动(如ext4驱动、ubifs驱动),确保内核能解析存储设备和文件系统。
【实战踩坑】:今天原本计划将制作成squashfs的rootfs镜像加密后,在ramdisk的init.sh.sqsh脚本中解密,结果发现没有办法读取到加密后的镜像,因为已经不是正常的squashfs文件系统格式,所以也挂载不上。
二 详细切换步骤(以init进程为例,兼容嵌入式常见场景)
步骤1:加载必要驱动模块(可选,按需执行)
若内核编译时未将目标存储设备/文件系统驱动编入内核(仅编译为ko模块),需在ramdisk中手动加载驱动,确保设备被识别。
[1]. 查看当前已加载驱动:通过 lsmod 确认驱动是否已加载,避免重复加载。
[2]. 加载驱动模块:进入ramdisk的驱动目录(通常为/lib/modules/内核版本/),执行加载命令:
insmod sdcard_drv.ko # 加载SD卡控制器驱动insmod ext4.ko # 加载ext4文件系统驱动
[3]. 验证设备识别:查看/dev目录下是否生成目标设备节点,如eMMC分区对应 /dev/mmcblk0p2,硬盘分区对应 /dev/sda1,存在则说明驱动加载成功。
步骤2:挂载目标根文件系统到临时目录
为避免覆盖当前ramdisk的根目录(/),需在ramdisk中创建临时挂载点,将目标根文件系统挂载到该目录,后续通过切换根目录指向完成迁移。
[1]. 创建临时挂载点:在ramdisk中创建空目录(如/mnt/root),作为目标根文件系统的临时挂载路径:
mkdir -p /mnt/root[2]. 挂载目标根文件系统:根据根文件系统格式和设备节点,执行挂载命令,核心参数需匹配文件系统特性:
# 示例1:挂载squashfs格式的eMMC分区mount -t squashfs /dev/mmcblk0p2 /mnt/root# 示例2:挂载ubifs格式的NAND Flash分区(需指定卷名)mount -t ubifs /dev/mtdblock3 /mnt/root -o volname=rootfs# 示例3:挂载ext4格式的硬盘分区mount -t ext4 /dev/sda1 /mnt/root关键参数说明:-t 指定文件系统类型,-o 追加选项(如ro只读挂载、volname指定ubifs卷名),首次挂载建议只读(ro),避免文件系统损坏。
[3]. 验证挂载结果:执行下面的命令
ls /mnt/root若能看到目标根文件系统的目录结构(bin、etc、lib、root等),说明挂载成功;若提示“no such file or directory”或“invalid filesystem”,需排查驱动、设备节点或文件系统完整性。
步骤3:迁移ramdisk中的关键资源到目标根文件系统
ramdisk中运行着当前init进程及子进程,切换根目录前需将必要资源(挂载点、文件描述符、进程上下文)迁移到目标根文件系统,避免切换后进程中断、资源丢失。核心迁移内容包括3类:
[1]. 迁移挂载点:将ramdisk中的/proc、/sys、/dev、/tmp等虚拟文件系统或临时目录,绑定挂载到目标根文件系统的对应位置,确保切换后系统能正常访问硬件和内核接口:
mount --bind /proc /mnt/root/proc # 绑定proc文件系统(进程信息)mount --bind /sys /mnt/root/sys # 绑定sysfs(设备驱动信息)mount --bind /dev /mnt/root/dev # 绑定dev(设备节点)mount --bind /tmp /mnt/root/tmp # 绑定tmp(临时文件)
说明:--bind 实现“目录映射”,让目标根文件系统的/proc等目录与ramdisk保持一致,无需重新挂载。
[2]. 迁移文件描述符:当前init进程及子进程的标准输入(0)、输出(1)、错误(2)均指向ramdisk的终端设备,需确保切换后仍能正常输出日志,无需额外操作(绑定/dev后自动继承)。
[3]. 迁移必要配置文件(可选):若ramdisk中有临时配置(如网络参数、驱动加载脚本),需复制到目标根文件系统的对应目录,确保切换后系统正常运行:
cp /etc/network/interfaces /mnt/root/etc/network/ # 复制网络配置步骤4:切换根目录(核心步骤,两种主流方式)
切换根目录的核心是“将系统根路径从ramdisk(/)指向目标根文件系统(/mnt/root)”,Linux提供两种工具:pivot_root(推荐,保留ramdisk可回滚)和switch_root(轻量,销毁ramdisk释放内存),按需选择。
方式1:使用pivot_root切换(推荐嵌入式场景)
pivot_root的核心逻辑是“交换根目录与旧根目录”,将原ramdisk(/)迁移到目标根文件系统的某个目录(如/oldroot),保留ramdisk可访问,便于切换失败后回滚。
[1]. 在目标根文件系统中创建旧根目录挂载点:
mkdir -p /mnt/root/oldroot[2]. 执行pivot_root切换:
pivot_root /mnt/root /mnt/root/oldroot命令含义:将根目录切换为/mnt/root(目标根文件系统),原根目录(ramdisk)挂载到/mnt/root/oldroot
[3]. 切换环境变量与工作目录:切换后需更新PATH变量(指向目标根文件系统的bin目录),并进入新根目录:
export PATH=/bin:/sbin:/usr/bin:/usr/sbin # 重置PATHcd / # 进入新根目录
方式2:使用switch_root切换(轻量场景)
说明:我们项目上使用的是switch_root切换,替换完销毁了ramdisk。
switch_root的逻辑是“直接替换根目录并销毁ramdisk”,切换后ramdisk被卸载,释放内存,但无法回滚,适用于内存紧张的嵌入式设备。
switch_root /mnt/root /bin/init命令含义:将根目录切换为/mnt/root,同时启动目标根文件系统中的init进程(/bin/init),并卸载原ramdisk。需注意:switch_root要求/mnt/root必须是挂载的文件系统,且目标根文件系统中存在init进程。
注意:有的文件系统init在根目录,如我们项目上init在根目录,所以切换命令是
switch_root /mnt /init步骤5:清理临时资源(切换成功后)
切换完成后,需卸载原ramdisk(旧根目录),释放内存资源,避免内存泄漏。仅针对pivot_root方式(switch_root已自动销毁ramdisk)。
[1]. 卸载旧根目录下的绑定挂载点:
umount /oldroot/procumount /oldroot/sysumount /oldroot/devumount /oldroot/tmp
[2]. 卸载原ramdisk:
umount /oldroot说明:若提示“device is busy”,需排查是否有进程仍占用/oldroot资源,执行 fuser -m /oldroot 查看占用进程,手动终止(如kill -9 进程ID)后再卸载。
[3]. 删除旧根目录挂载点:
rm -rf /oldroot步骤6:启动目标根文件系统的init进程
切换根目录后,需启动目标根文件系统中的init进程(或systemd),完成系统后续启动(加载服务、启动应用程序等),替代ramdisk中的临时init进程。
[1]. 验证新根目录的init进程:确认目标根文件系统中存在init进程(通常为/bin/init、/sbin/init,或systemd的/usr/lib/systemd/systemd)。
[2]. 启动init进程:
exec /bin/init关键说明:使用exec命令启动init,会替换当前进程(ramdisk中的init),避免产生僵尸进程,确保新init进程成为系统1号进程。
[3]. 系统正常启动:init进程启动后,会执行/etc/inittab(sysvinit)或systemd配置文件,加载网络、存储、应用等服务,最终完成整个系统启动。
三 关键工具对比(pivot_root/switch_root)

四 常见问题与排查方法
[1].问题1:目标设备节点不存在(如/dev/mmcblk0p2缺失)
排查步骤:
>>确认驱动已加载(lsmod查看);
>>执行 cat /proc/partitions 查看内核识别的分区,若无分区需检查存储设备硬件连接;
>>重启系统重新加载驱动。
[2].问题2:挂载目标根文件系统提示“invalid filesystem”
排查步骤:
>>确认文件系统类型与-t参数一致(如误将ubifs按ext4挂载);
>>检查根文件系统镜像是否损坏(重新烧录镜像);
>>加载对应文件系统驱动(如ubifs.ko)。
[3].问题3:pivot_root提示“invalid argument”
排查步骤:
>>目标根目录(/mnt/root)必须是挂载的文件系统,不能是ramdisk中的普通目录;
>>旧根目录挂载点(/mnt/root/oldroot)必须存在且为空。
[4].问题4:切换后init进程启动失败(提示“no such file or directory”)
排查步骤:
>>确认目标根文件系统中init进程路径正确(如/bin/init是否存在);
>>检查init进程权限(是否为可执行,chmod +x /bin/init);
>>排查目标根文件系统的lib库是否缺失(init依赖的库需存在于/lib目录)。
五 总结
整个切换流程的核心逻辑的是“过渡-准备-切换-清理”:ramdisk作为过渡支撑,完成驱动加载和设备识别后,通过挂载目标根文件系统、迁移资源、切换根目录,最终交付给永久根文件系统运行。
实际应用中需根据嵌入式设备的存储类型(eMMC、NAND、硬盘)和文件系统格式(ext4、ubifs)调整挂载命令和驱动,同时优先选择pivot_root提升切换安全性,量产场景可选用switch_root优化内存占用。
以上为全文内容。
这里是女程序员的笔记本
15年+嵌入式软件工程师兼二胎宝妈
分享读书心得、工作经验,自我成长和生活方式。
希望我的文字能对你有所帮助