根檔案系統掛載過程—基於linux3.10
本文基於linux3.10某一嵌入式系統,該檔案系統的配置選項設定如下:
圖1.1 根檔案系統配置選項設定
兩行配置如下:
[*] Initial RAMfilesystem and RAM disk (initramfs/initrd) support
(usr/rootfs.cpio.gz)Initramfs source file(s)
這兩行的意義是啟用initramfs檔案系統,並且將原始碼目錄下的usr/roorfs.cpio.gz作為根檔案系統。核心會將該檔案系統載入到記憶體並且掛載該檔案系統做為根檔案系統。
在windos下通常有C盤、D盤之類的分割槽,可以把linux下的根檔案系統“/”理解成C盤這樣的裝置,對於這裡所說的嵌入式情景,這個裝置就是一整塊NAND FLASH,看起來有點類似圖1.1所示的情況,在掛載根檔案系統時會呼叫rootfs_mount讀取flash上的超級塊資訊,並依據該資訊構建VFS層的超級塊資訊。超級塊資訊獲取是通過MTD層介面實現的,實際上底層是通過CPU 的NAND FLASH控制器來實現對NAND FLASH讀寫的。
圖1.2 只有一個根節點的NAND裝置
首先是start_kernel()函式,該函式在啟動那些事中有過說明,該函式呼叫vfs_caches_init()函式,接下來呼叫inode_init()初始化索引節點,然後呼叫mnt_init()函式掛載根檔案系統,mount_init()函式呼叫init_rootfs函式。
圖1.3 根檔案系統掛載函式呼叫流程
呼叫init_rootfs函式並未傳遞引數,所以分析起來容易很多。bdi是backing_dev_info的縮寫,bdi_init用於初始化後備儲存器的一些欄位,這些欄位包括回寫連結串列、回寫鎖等,關係到讀寫策略,和掛載關係並不大。register_filesystem在《
<fs/ramfs/inode.c> 277 int __init init_rootfs(void) 278 { 279 int err; 280 err = bdi_init(&ramfs_backing_dev_info); 281 if (err) 282 return err; 283 284 err = register_filesystem(&rootfs_fs_type); 285 if (err) 286 bdi_destroy(&ramfs_backing_dev_info); 287 288 return err; 289 }
對應這裡的根檔案系統的結構定義如下:
<fs/ramfs/inode.c>
static struct file_system_type rootfs_fs_type = {
.name = "rootfs",
.mount = rootfs_mount,
.kill_sb = kill_litter_super,
};
init_mount_tree()也沒有需要傳遞的引數,get_fs_type函式在VFS那篇文章提過,這裡就是獲得引數“rootfs”對應的檔案系統型別,如果發現當前檔案系統並未註冊該檔案系統則會嘗試以module的方式掛載該檔案系統。一種檔案系統只能註冊一次,但是多個裝置可以掛載為同一種檔案系統型別的裝置。
<fs/namespace.c>
2686 static void __init init_mount_tree(void)
2687 {
2688 struct vfsmount *mnt;
2689 struct mnt_namespace *ns;
2690 struct path root;
2691 struct file_system_type *type;
2692
2693 type = get_fs_type("rootfs");
2694 if (!type)
2695 panic("Can't find rootfs type");
2696 mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
2697 put_filesystem(type);
2698 if (IS_ERR(mnt))
2699 panic("Can't create rootfs");
2700
2701 ns = create_mnt_ns(mnt);
2702 if (IS_ERR(ns))
2703 panic("Can't allocate initial namespace");
2704
2705 init_task.nsproxy->mnt_ns = ns;
2706 get_mnt_ns(ns);
2707
2708 root.mnt = mnt;
2709 root.dentry = mnt->mnt_root;
2710
2711 set_fs_pwd(current->fs, &root);
2712 set_fs_root(current->fs, &root);
2713 }
2694行判斷是否找到了該檔案系統,因為後面mount根檔案系統時是需要其mount方法的,而mount方法存在於檔案系統型別中,所以2693行必須要先查詢一下,如果沒找到則2695行就顯示panic,這是核心非常嚴重的錯誤,這種錯誤預示著裝置將宕機。2696行是實際意義上的掛載。
struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
struct mount *mnt;
struct dentry *root;
if (!type)
return ERR_PTR(-ENODEV);
/*分配並初始化一個mount結構體,該結構體儲存了若干和mount相關的資訊,這些資訊包括,掛載點(目錄項)、父掛載點、若干掛載連結串列等*/
mnt = alloc_vfsmnt(name);
if (!mnt)
return ERR_PTR(-ENOMEM);
if (flags & MS_KERNMOUNT)
mnt->mnt.mnt_flags = MNT_INTERNAL;
root = mount_fs(type, flags, name, data); // 真正的掛載函式,如果掛載成功,則會返回目錄項
if (IS_ERR(root)) {
free_vfsmnt(mnt);
return ERR_CAST(root);
}
mnt->mnt.mnt_root = root; //掛載點目錄項
printk(KERN_EMERG "mnt->mnt.mnt_root:%s", root->d_iname);
mnt->mnt.mnt_sb = root->d_sb;//掛載點超級塊
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
mnt->mnt_parent = mnt;//父目錄資訊
br_write_lock(&vfsmount_lock);
//將新掛載的根檔案系統新增到超級塊的掛載連結串列上
list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
br_write_unlock(&vfsmount_lock);
return &mnt->mnt;
}
mount_fs的工作就是完成根檔案系統的掛載,實際上就是讀取FLASH上超級塊並填充VFS層自己的超級塊。
<fs/super.c>
1086 struct dentry *
1087 mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
1088 {
1089 struct dentry *root;
1090 struct super_block *sb;
1091 char *secdata = NULL;
1092 int error = -ENOMEM;
…
1103 //這裡就是呼叫根檔案系統的mount方法。
1104 root = type->mount(type, flags, name, data);
1105 if (IS_ERR(root)) {
1106 error = PTR_ERR(root);
1107 goto out_free_secdata;
1108 }
1109 sb = root->d_sb;
…
1137 return ERR_PTR(error);
1138 }
rootfs_mount 函式實際上就是對mount_nodev的封裝,很容易理解這裡命名方法,因為該檔案系統是沒有實際的物理裝置對應的,其實際上只存在於記憶體中。最後一個引數ramfs_fill_super指定了超級塊的填充方法。
static struct dentry *rootfs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super);
}
mount_nodev首先呼叫sget獲得超級塊,然後呼叫指標函式引數fill_super對其進行填充。
struct dentry *mount_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int))
{
int error;
struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
if (IS_ERR(s))
return ERR_CAST(s);
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
if (error) {
deactivate_locked_super(s);
return ERR_PTR(error);
}
s->s_flags |= MS_ACTIVE;
return dget(s->s_root);
}