1. 概念
VFS,即Virtual File System,在Linux中指虚拟文件系统
VFS封装了底层文件系统的所有功能和抽象,同时负责把应用层的请求转发给特定的文件系统
VFS抽象四个基本概念
- 超级块(superblock): 存放文件系统控制信息
- 索引节点(inode): 存放具体文件的一般信息, 用来标识存储介质上的文件
- 文件(file): 存放已打开的文件和进程之间交互的信息
- 目录项(dentry): 存放文件的名称和文件的路径
2. 数据结构
vfs中重要数据结构包括superblock、inode、file和dentry,其他重要数据结构包括
- file_system_type: 用来描述不同的文件系统
- vfsmount: 描述系统中已经挂载的文件系统信息, 用于对超级块和inode进行跟踪
superblock主要成员如下
成员 | 作用 |
s_list | 指向下一个superblock,所有的superblock组成一个链表,头指针保存在super_blocks变量中 |
s_fs_info | 指向特定文件系统的superblock信息,比如ext2,这个指针指向ext2_sb_info结构 |
s_dirt | 因为superblock既存在于内存中也存在于磁盘中,所以需要这个标记标示内存中的数据是否和磁盘中的同步 |
s_root | 文件系统根目录的dentry,表示这个文件系统的mount点
|
s_inodes | 指向所有inodes组成的链表的头指针 |
s_files | 指向所有file对象组成的链表的头指针 |
s_op | 超级块操作函数集,struct super_operations类型,包括alloc_inode,destroy_inode,put_super等函数,用来管理这个文件系统中的 inode 的函数 |
inode的主要成员如下
成员 | 作用 |
i_dentry | 指向本inode的所有dentry对象组成的链表的头指针 |
i_ino | 指向特定文件系统的superblock信息,比如ext2,这个指针指向ext2_sb_info结构 |
i_nlink | inode的硬连接引用计数 |
i_count | 进程对inode的引用计数 |
i_uid/i_gid | 所属用户/组的id |
i_atime | inode访问时间,类型域有i_mtime和i_ctime |
i_mode | inode的权限 |
i_size | 文件大小(单位:字节) |
i_data | 数据地址索引,struct address_space结构类型 |
i_mapping | 内存索引页面,struct address_space结构类型指针,指向i_data(?) |
i_blocks | 所占的block数 |
i_bytes | 最后一个block使用了的字节数 |
i_sb_list | 指向super_block中s_inodes链表中本inode的下一个inode |
i_op | 索引节点操作函数集,inode_operations类型,定义直接在 inode 上执行的操作 |
i_cdev/i_pipe/i_bdev | 非特殊文件时,inode所存储的设备 |
i_rdev | 非特殊文件时,所存储设备的设备号 |
dentry的主要成员如下
成员 | 作用 |
d_name | 文件名 |
d_inode | 指向的inode |
d_parent | 父目录的dentry对象 |
d_count | 目录项对象使用计数器 |
d_child | 对于目录来说,和本dentry相同父目录的目录dentry对象链表的头指针 |
d_subdirs | 对于目录来说,当前dentry下的子目录dentry对象链表的头指 |
d_alias | 指向相同inode的所有dentry对象构成的链表的头指针 |
d_op | 目录项操作表,dentry_operations类型 |
d_sb | 文件的超级块对象 |
file的主要成员如下
成员 | 作用 |
f_dentry | 指向当前file对象关联的dentry对象 |
f_vfsmnt | |
f_pos | 文件指针 |
f_op | 定义与文件和目录相关的方法(即标准系统调用POSIX标准文件I/O管理函数),file_operations结构类型 |
file_system_type的主要数据结构
成员 | 作用 |
name | 文件系统的名称 |
fs_flags | 一些特殊标记,如FS_REQUIRES_DEV |
mount | 本文件系统的挂载方法,执行kern_mount函数时调用 |
kill_sb | 卸载文件系统超级块的方法 |
next | file_system_type类型单项链表,指向其他文件系统,单链表的表头由全局变量file_systems表示 |
fs_supers | 双向链表,指向本文件系统所支持的super_block |
vfsmount的主要数据结构
成员 | 作用 |
mnt_parent | 上一层挂载点 |
mnt_mounts | 子文件系统的链表头 |
mnt_child | 指向上一层挂载点mnt_mounts形成双链表的兄弟结点 |
mnt_mountpoint | 指向挂载点dentry结构的指针 |
mnt_root | 指向本文件系统的根路径dentry |
mnt_sb | 指向文件系统超级块 |
mnt_flags | 挂载选项,如MNT_NODEV,MNT_READONLY等 |
mnt_list | 指向vfsmount结构所形成链表的头指针 |
TIP: 通常,superblock, inode会保存至存储介质;而dentry ,file仅存在于内存中
3. 文件系统实现
下面以实现某个简单文件系统为例
static struct vfsmount *fixfs_mnt; static struct file_system_type fixfs_fs_type = { .owner = THIS_MODULE, .name = "fixfs", .mount = fixfs_mount, .kill_sb = fixfs_kill_sb, }; static int __init init_fixfs_fs(void) { int ret; /* fixfs specify init */ ret = register_filesystem(&fixfs_fs_type); if (!ret) { fixfs_mnt = kern_mount(&fixfs_fs_type); if (IS_ERR(fixfs_mnt)) { fixfs_mnt = NULL; unregister_filesystem(&fixfs_fs_type); } } /* error handle specs */ } static void __exit exit_fixfs_fs(void) { unregister_filesystem(&fixfs_fs_type); /* fixfs specify cleanup */ } module_init(init_fixfs_fs); module_exit(exit_fixfs_fs);
4. 特殊文件系统
Linux中有一些特殊文件系统,有特殊的用途,基于内存实现
主要包括rootfs、proc、sysfs、swap、tmpfs等
4.1 rootfs
roofs初始化主要过程如下
vfs_caches_init
mnt_init
init_rootfs
init_mount_tree
roofs初始化后创建一个基于内存虚拟的根文件系统,后面会指向真正的根文件系统(基于block_dev)
上面两个函数执行完成后,rootfs的挂载点默认为"/",init_task进程的根目录和当前目录为"/"。
下面介绍实际的根文件系统挂载
在Linux中,允许实际的根文件系统存放在不同的地方,如硬盘分区,软盘,NFS或保存在ramdisk中,此时需要在内核中指定变量ROOT_DEV或由bootloader来传递root提供包含根文件系统的设备号
也可以将实际的根文件系统编进内核
实际根文件系统的挂载在函数prepare_namespace中,该函数必须在do_basic_setup之后
之所以在do_basic_setup之后,因为do_basic_setup完成了模块的加载(do_initcalls),也包括设备驱动程序,这样后面在具体设备上读取根文件系统才能成功。
在do_initcalls函数会执行rootfs_initcall,
对于initrd类型(initrd机制)会调用populate_rootfs,否则调用default_rootfs
populate_rootfs主要完成initrd的检测工作,检查并解压CPIO Initrd或Initramfs或Image-Initrd
对于initramfs和cpio-initrd的,都会将文件系统解压到根文件系统,此时将不会执行prepare_namespace
default_rootfs创建目录(/dev,/root)及节点(/dev/console)
prepare_namespace首先会解析内核参数root(由bootloader传递)并生成ROOT_DEV
然后尝试加载Image-Initrd(/initrd.image)
然后会尝试加载软盘0分区上的根文件系统
最后通过mount_root依次加载NFS,软盘1分区及块设备上的根文件系统
当成功加载一种根文件系统后,会进行根文件系统的切换
当实际根文件系统成功挂载后,init_post会执行一些清理工作并执行init程序切换至用户空间