1. 程式人生 > >根檔案系統掛載過程—基於linux3.10

根檔案系統掛載過程—基於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在《

虛擬檔案系統 (VFS)-基於linux3.10》明確指出一個檔案系統想要使用必須先載入該檔案系統,表示該檔案系統的引數rootfs_fs_type包括了該檔案檔案系統的mount方法。

<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);
}