mount过程分析之一(基于3.16.3内核)【转】
转自:https://blog.csdn.net/zr_lang/article/details/39963253
一直想写有些关于文件系统的博文,但是由于近一年来实在太忙,所以没有时间写。前几日赶上放国庆节假期,回来后又正好处在RHEL-6.6刚发布,RHEL-7.1还没有开始繁重工作的阶段,所以抽出一些时间准备写一些东西。不知道是否能坚持长久,可能在忙起来后会拖后进度吧。反正也不着急,慢慢写。
既然要分析就要选择一个确定的linux版本进行说明,目前最新的linux稳定版是3.16.3,就选这个内核了。之后的文章都以这个版本为基础。
研究linux fs的入手点应该是哪呢?可能仁者见仁,智者见智。我一直以来也都是零零散散的翻阅着有关super block, inode, dentry, file等等内容的各种代码和资料,但是知识始终是散乱的,无法拼凑成完整的体系。现在我要试图整理顺一个整套的文件系统知识,我决定从file_system_type入手。
本文是要尽力将我已经掌握的分块的知识串连起来,所以至少了解linux vfs的几个结构是必须的,super_block, inode, dentry, file等。
在include/linux/fs.h里定义着几个基本数据结构——block_device, file_system_type, inode和file。其中block_device一般和块设备操作,如硬盘、分区等有关,文件系统的操作多半都会反映到硬盘等块设备上的操作,如读取super block、读写数据等,所以block_device掌管着文件系统的底层设备。由于我们从file_system_type开始研究,所以这里先不管block_device。
来看file_system_type的结构:
- struct file_system_type {
- const char *name;
- int fs_flags;
- #define FS_REQUIRES_DEV 1
- #define FS_BINARY_MOUNTDATA 2
- #define FS_HAS_SUBTYPE 4
- #define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */
- #define FS_USERNS_DEV_MOUNT 16 /* A userns mount does not imply MNT_NODEV */
- #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
- struct dentry *(*mount) (struct file_system_type *, int, const char *, void *);
- void (*kill_sb) (struct super_block *);
- struct module *owner;
- struct file_system_type * next;
- struct hlist_head fs_supers;
- // ....
- //为了说明方便,此处省略若干锁相关变量。
- //....
- }
name: 文件系统的名字,如xfs, ext2等
fs_flags: 说明文件系统的类型,下面的宏定义代表了它的几种类型:
FS_REQUIRES_DEV: 文件系统必须在物理设备上。
FS_BINARY_MOUNTDATA: mount此文件系统时(参见mount_fs函数 - fs/super.c)需要使用二进制数据结构的mount data(如每个位域都有固定的位置和意义),常见的nfs使用这种mount data(参见struct nfs_mount_data结构 - include/uapi/linux/nfs_mount.h)。
FS_HAS_SUBTYPE: 文件系统含有子类型,最常见的就是FUSE,FUSE本是不是真正的文件系统,所以要通过子文件系统类型来区别通过FUSE接口实现的不同文件系统。
FS_USERNS_MOUNT: 文件系统每次挂载都后都是不同的user namespace,如用于devpts。
FS_USERNS_DEV_MOUNT: user namespace挂载支持MNT_DEV, 即非nodev模式。
FS_RENAME_DOES_D_MOVE: 文件系统将把重命名操作reame()直接按照移动操作d_move()来处理,主要用于网络万件系统。
mount: 代替早期的get_sb(),用户挂载此文件系统时使用的回调函数。
kill_sb: 删除内存中的super block,在卸载文件系统时使用。
owner: 指向实现这个文件系统的模块,通常为THIS_MODULE宏。
next: 指向文件系统类型链表的下一个文件系统类型。
fs_supers: 具有同样此文件系统类型的超级块结构,都串连在这个表头下。
有了file_system_type,现在就是如何使用file_systems_type了。file_system_type的基本操作都在fs/filesystems.c文件里,文件不长,可以简短的浏览一下都有那些操作函数。最主要的变量莫过于此文件内的全局变量:
// 此变量是文件系统类型单链表的头指针
static struct file_system_type *file_systems;
而其中最主要的函数莫过于:
- int register_filesystem(struct file_system_type * fs)
- {
- int res = 0;
- struct file_system_type ** p;
- BUG_ON(strchr(fs->name, '.'));
- if (fs->next)
- return -EBUSY;
- write_lock(&file_systems_lock);
- // 遍历全局file_systems链表,尝试查找本次要注册文件系统名。
- p = find_filesystem(fs->name, strlen(fs->name));
- if (*p) // 如果不为NULL,则说明找到了重名的文件系统。注册失败。
- res = -EBUSY;
- else // 如果返回NULL,说明已经到链表结尾,可以注册此文件系统。
- *p = fs; // 将此文件系统链接到链表结尾。
- write_unlock(&file_systems_lock);
- return res;
- }
mount/get_sb和kill_sb方法
我觉得file_system_type,或者说register文件系统时就是要完成两个主要的事情:
1. 告诉kernel自己叫什么(一个唯一的name)
2. 告诉kernel怎么使用自己的超级块信息,以便完成挂载自己的操作(过去的get_sb现在的mount)。
所以mount和kill_sb回调函数就是file_system_type中最需要重点实现的了,因为这里涉及到每个文件系统的具体实现,所以我们就不详细的一一说明,只说一下大概的实现死路。fs/下实现着不同的文件系统,每一个目录名基本上就代表一个具体文件系统的实现,以xfs为例,fs/xfs/下就是xfs文件系统的实现。而file_system_type结构一般被定义在super.c里(一般,不是所有)。那么看一下fs/xfs/xfs_super.c里如何定义xfs的file_system_type:
- static struct file_system_type xfs_fs_type = {
- .owner = THIS_MODULE,
- .name = "xfs",
- .mount = xfs_fs_mount,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
- }
owner说明xfs模块本身拥有这个file_system_type。
name是xfs。
fs_flags是FS_REQUIRES_DEV,表明xfs一定要被使用在物理设备上。
接下来mount=xfs_fs_mount,说明 xfs_fs_mount这个函数实现了xfs的具体mount操作。搜索xfs_fs_mount,在本文件里看到了它的实现:
- STATIC struct dentry *
- xfs_fs_mount(
- struct file_system_type *fs_type,
- int flags,
- const char *dev_name,
- void *data)
- {
- return mount_bdev(fs_type, flags, dev_name, data, xfs_fs_fill_super);
- }
看起来就是一个mount_bdev()函数,但是看参数可以看到一些重要的东西
fs_type不用多说,携带file_system_type的信息,这里传递它主要是因为它携带了super block的链表和很多锁变量。
flags文件系统的通用挂载选项。
dev_name是mount操作时的设备名,如/dev/sda1。后面会用到这个设备名找到对应的设备信息,从而从中获得super block。
data是挂载时指定的挂载选项信息。
xfs_fs_fill_super是一个由xfs特定实现的fill_super方法,用来根据xfs文件系统的特性解析mount data并继续填充super block的字段,并且初始化挂载点的根索引节点对象和目录项对象。
对于mount的具体过程将在后续内容中详细介绍。
总结:
为什么我认为file_system_type是学习linux fs的开始,因为注册文件系统是让一个具体文件系统能够生效的第一步,而注册文件系统实际就是注册了一个file_system_type实例。如xfs文件系统,加载xfs模块是使用xfs的必要条件,加载xfs模块时就会将xfs的file_system_type注册到kernel中,当然xfs要先具体化一个自己的file_system_type,然后注册。
当一个文件系统注册进内核后,它并没有被使用,只有mount这个文件系统后,这个文件系统才真正工作,我们一般这样初始化并使用一个文件系统:
mkfs.xfs /dev/sda1
mount -t xfs /dev/sda1 /mnt
mkfs是用户态工具,根据每个文件系统的不同实现有不同的mkfs方法。对于硬盘来说,mkfs就是向硬盘中按照某个文件系统的格式写入文件系统的信息到硬盘中,如文件系统类型,block
size,bitmap,
inode索引等,这些内容主要组成了一个文件系统的超级块,本来这些信息是很难理解的,单是对于文件系统本身来说他们就是一个个有效的数据。
在mount文件系统时,这个文件系统注册的mount回调函数(过去的get_sb函数)就有用了,它由具体文件系统实现,可以从设备上(如磁盘)读取文件系统的超级块信息,用以完整整个mount操作。(关于mount操作我们后面叙述)
所以说file_system_type主要就是完成两件事,一个是告诉系统自己叫什么,一个是告诉系统怎样解析自己特有的数据结构完成mount操作。
http://blog.csdn.net/zr_lang/article/details/40325241 (mount 七)
http://blog.csdn.net/zr_lang/article/details/40343899 (mount 六)
http://blog.csdn.net/zr_lang/article/details/40115013 (mount 五)
http://blog.csdn.net/zr_lang/article/details/40080979 (mount 四)
http://blog.csdn.net/zr_lang/article/details/40049305 (mount 三)
http://blog.csdn.net/zr_lang/article/details/40002285 (mount 二)
http://blog.csdn.net/zr_lang/article/details/39963253 (mount 一)