Linux 內核啟動過程中,根文件系統的掛載是最關鍵、最複雜的環節之一。本文從內核源碼角度,系統性剖析 rootfs 的類型、掛載流程、虛擬文件系統初始化,以及最終切換到真實根文件系統的完整鏈路。
| 類型 | 特徵 | 典型場景 |
|---|---|---|
| 虛擬根文件系統 | 運行於記憶體,內核啟動早期提供最小環境 | initramfs、initrd、嵌入式系統 |
| 真實根文件系統 | 持久化儲存於磁碟/Flash/網路,包含完整用戶空間 | ext4/xfs/btrfs、NFS、SquashFS |
void __init mnt_init(void)
{
...
init_rootfs(); // 註冊 rootfs 文件系統類型
init_mount_tree(); // 掛載 rootfs 並建立初始命名空間
}
int __init init_rootfs(void)
{
int err = bdi_init(&ramfs_backing_dev_info);
if (err) return err;
err = register_filesystem(&rootfs_fs_type); // 向 VFS 註冊
if (err) bdi_destroy(&ramfs_backing_dev_info);
return err;
}
rootfs_fs_type 定義於 fs/ramfs/inode.c,實際掛載 ramfs(或配置為 tmpfs)。
static void __init init_mount_tree(void)
{
struct vfsmount *mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
// 建立掛載命名空間
struct mnt_namespace *ns = kmalloc(sizeof(*ns), GFP_KERNEL);
// 設定 init_task 的根目錄與當前工作目錄為 "/"
set_fs_pwd(current->fs, &root);
set_fs_root(current->fs, &root);
}
此時 / 指向記憶體中的 rootfs,**尚未**掛載真實磁碟分區。
static int __init populate_rootfs(void)
{
char *err = unpack_to_rootfs(__initramfs_start, __initramfs_end - __initramfs_start, 0);
if (err) panic(err);
if (initrd_start) {
// 處理傳統 initrd 映像
...
}
return 0;
}
unpack_to_rootfs() 將內核映像附帶的 cpio 歸檔解壓至 rootfs,釋放 /init、驅動模組、啟動腳本等。
核心邏輯位於 init/do_mounts.c:
root=、rootfstype=、rootflags=、rootdelay=/dev/、udev 事件、rootdelay 超時sys_mount() 掛載至 /root 或 /sysrootsys_chdir("/root") + sys_mount(".", "/", NULL, MS_MOVE, NULL) + sys_chroot(".")run_init_process("/sbin/init")
// fs/namespace.c
struct mount {
struct hlist_node mnt_hash;
struct mount *mnt_parent; // 父掛載點
struct dentry *mnt_mountpoint; // 掛載點目錄項
struct vfsmount mnt; // VFS 掛載結構
...
};
// include/linux/fs.h
struct vfsmount {
struct dentry *mnt_root; // 根目錄項
struct super_block *mnt_sb; // 超級塊
int mnt_flags;
...
};
對於 Flash 直啟系統(無 initramfs):
root=/dev/mtdblock2 rootfstype=jffs2/sbin/init (通常為 BusyBox)| 現象 | 可能原因 | 診斷方法 |
|---|---|---|
| Kernel panic: VFS: Unable to mount root fs | root= 參數錯誤、驅動缺失、initramfs 損壞 | 檢查 bootloader 參數、確認驅動編譯內建 (*=y) |
| 掛載後進入 initramfs shell | 真實根分區未就緒、UUID 不匹配、fstab 錯誤 | blkid 核對 UUID、檢查 /proc/cmdline |
| 切換根失敗 | /sbin/init 缺失、動態鏈接庫缺失、SELinux 阻擋 | ls -l /root/sbin/init、ldd /root/sbin/init |
# 1. 內核命令行加入調試參數
rd.debug rd.shell rd.break=pre-mount
# 2. 在 initramfs 中手動檢查
blkid
ls /dev/mapper/
cat /proc/cmdline
mount /dev/sda2 /mnt && ls /mnt
# 3. 重新生成 initramfs
dracut -f -v # RHEL/CentOS/Fedora
update-initramfs -u -k all # Debian/Ubuntu
mkinitcpio -P # Arch