虚拟文件系统(vfs)是Linux内核的子系统,其设计目的是对用户层(系统调用)屏蔽底层(各特定文件系统)实现的复杂性,对底层提供统一的接口与数据结构,使得能衔接各个特定文件系统(如ext2、ext3、fat32等)的对接,是用户层与文件系统层之间的抽象层。
与vfs层相关的主要数据结构有以下几个:超级块对象super_block、索引节点对象inode、目录项dentry、文件对象file、文件系统类型file_system_type、挂载的文件系统vfsmount等。
下面简单介绍下从磁盘分区格式化到文件挂载和文件操作的几个过程,与vfs层以及具体文件系统层相对应的工作机制流程,见下表:
命令 | 功能 | 底层流程 | 原理及作用 |
mkfs.ext3 /dev/sda4 | 格式化磁盘分区,将文件系统信息写入到磁盘超级块,为以特定的文件系统格式读写做铺垫 | 格式化时的读写磁盘不需要通过文件系统层,即跨过文件系统,直接写入磁盘(通过块设备驱动写入,与dd命令类似)。 | strace跟踪dd命令open(/dev/sda4) = 3 read(3),读写块设备/dev/sda4的方式就是跨过文件系统,直接写入磁盘 |
insmod ext3.ko | 内核装载ext3驱动模块,向内核注册ext3文件系统。 | 文件系统登记在file_system_type全局链表中 | 提供ext3文件系统各种函数,如填充操作块,索引节点操作函数,如分配ext4_alloc_inode、读写索引节点 |
mount -t ext3 /dev/sda4 /mnt/abc | 建立在内核已经注册ext3文件系统的基础之上,挂载sda4分区 | 建立vfs_mount对象,该对象代表挂载的文件系统(挂载点) | file_system_type中的方法get_sb调用alloc_super函数填充内存超级块对象(vfs_mount中相关的超级块对象) |
使用Linux文件系统是这样的情景,我们得先分区(如划分/dev/sda4),然后格式化成具体文件系统类型(如ext3),再然后挂载到某个目录(如/mnt),最后在mnt目录及层层子目录下进行相关的文件操作(如打开创建、读写、关闭、删除等)。
1、格式化
进行ext3格式化时会:Writing superblocks and filesystem accounting information,这是执行mkfs.ext3输出的信息,格式化会将特定文件系统控制信息写入到磁盘分区超级块,待mount磁盘分区时被读出,下面介绍mount。
2、挂载mount
我们知道要使用一个文件系统首先要 mount 才可使用。我们清楚了以上结构后,接着来看 vfs_kern_mount() 函数,这个函数是内核最终实现 mount 的函数,调用关系如下:
mnt = alloc_vfsmnt(name); //建立vfs_mount对象
type->get_sb(type, flags, name, data, mnt); //调用文件系统的方法从磁盘超级块中获取超级块
ext3_fill_super //填充超级块 sb->s_op = &ext3_sops; //超级块操作对象,包括ext3_read_inode()
sb->s_root = d_alloc_root(root); //超级块的根目录项
struct qstr name = { .name = "/", .len = 1 };
mnt->mnt_sb = sb; //将超级块关联挂载的文件系统
mnt->mnt_root = dget(sb->s_root); //该文件系统的根目录项对象,就是根目录/
mnt->mnt_mountpoint = mnt->mnt_root; //安装点的目录项对象,也即上表挂载的abc目录
3、打开(创建)文件
下面是open系统调用在内核中的实现 sys_open:
do_filp_open
path_walk
do_lookup
real_lookup
ext3_lookup(parent->i_op->lookup) //查找inode
struct inode *iget(parent->i_sb, unsigned long ino) //获取inode sb->s_op->read_inode(inode); //ext3_read_inode() //从磁盘获取子inode填充
nameidata_to_filp
__dentry_open(nd->path.dentry, nd->path.mnt, flags, filp)
f->f_path.dentry = dentry;
f->f_path.mnt = mnt;
f->f_op = fops_get(inode->i_fop); //将inode的文件操作对象赋给打开的文件对象的操作对象,如ext3_file_operations
if (!open && f->f_op) //如果open实现不存在
open = f->f_op->open;
if (open)
error = open(inode, f);
4、打开的文件对象file
用户层read->sys_read->file->read->do_sync_read,do_sync_read为具体文件系统(如ext3)ext3_sops操作对象中read的实现。
5、文件系统注册到内核file_system_type全局链表中
module_init(init_ext3_fs) //ext3 文件驱动加载 super.c 提供注册文件系统方法等。
init_ext3_fs
register_filesystem(&ext3_fs_type); //插入file_system_type全局链表
6、ext3相关数据结构
//ext3文件系统类型数据结构,来自ext3.ko 中super.c文件
static struct file_system_type ext3_fs_type = {
.owner = THIS_MODULE,
.name = "ext3",
.get_sb = ext3_get_sb,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
//ext3超级块操作对象,来自ext3.ko 中super.c文件
static struct super_operations ext3_sops = {
.alloc_inode = ext3_alloc_inode,
.destroy_inode = ext3_destroy_inode,
.read_inode = ext3_read_inode,
.write_inode = ext3_write_inode,
.dirty_inode = ext3_dirty_inode,
.delete_inode = ext3_delete_inode,
.put_super = ext3_put_super,
.write_super = ext3_write_super,
.sync_fs = ext3_sync_fs,
.write_super_lockfs = ext3_write_super_lockfs,
.unlockfs = ext3_unlockfs,
.statfs = ext3_statfs,
.remount_fs = ext3_remount,
.clear_inode = ext3_clear_inode,
.show_options = ext3_show_options,
#ifdef CONFIG_QUOTA
.quota_read = ext3_quota_read,
.quota_write = ext3_quota_write,
#endif
};
//ext3文件操作对象,来自ext3.ko 中super.c文件
const struct file_operations ext3_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = ext3_file_write,
};