参考《Linux内核设计与实现》

虚拟文件系统(VFS)它是linux核心和详细I/O一个普通的访问接口之间的包装设备,通过这层界面,linux内核能够以同一的方式訪问各种I/O设备。

虚拟文件系统本身是linux内核的一部分,是纯软件的东西。并不须要不论什么硬件的支持。

1. 虚拟文件系统的作用

虚拟文件系统(VFS)是linux内核和存储设备之间的抽象层,主要有下面优点。

- 简化了应用程序的开发:应用通过统一的系统调用訪问各种存储介质

- 简化了新文件系统增加内核的过程:新文件系统仅仅要实现VFS的各个接口就可以,不须要改动内核部分

 2. 虚拟文件系统的4个主要对象虚拟文件里的4个主要对象,具体每一个对象的含义參见例如以下的具体介绍。

 2.1 超级块

超级块(super_block)主要存储文件系统相关的信息,这是个针对文件系统级别的概念。

它一般存储在磁盘的特定扇区中,可是对于那些基于内存的文件系统(比方proc,sysfs),超级块是在使用时创建在内存中的。

超级块的定义在:<linux/fs.h>

/*
* 超级块结构中定义的字段许多,
* 这里仅仅介绍一些重要的属性
*/
struct super_block {
struct list_head s_list; /* 指向全部超级块的链表 */
const struct super_operations *s_op; /* 超级块方法 */
struct dentry *s_root; /* 文件夹挂载点 */
struct mutex s_lock; /* 超级块信号量 */
int s_count; /* 超级块引用计数 */ struct list_head s_inodes; /* inode链表 */
struct mtd_info *s_mtd; /* 存储磁盘信息 */
fmode_t s_mode; /* 安装权限 */
}; /*
* 当中的 s_op 中定义了超级块的操作方法
* 这里仅仅介绍一些相对重要的函数
*/
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb); /* 创建和初始化一个索引节点对象 */
void (*destroy_inode)(struct inode *); /* 释放给定的索引节点 */ void (*dirty_inode) (struct inode *); /* VFS在索引节点被改动时会调用这个函数 */
int (*write_inode) (struct inode *, int); /* 将索引节点写入磁盘。wait表示写操作是否须要同步 */
void (*drop_inode) (struct inode *); /* 最后一个指向索引节点的引用被删除后,VFS会调用这个函数 */
void (*delete_inode) (struct inode *); /* 从磁盘上删除指定的索引节点 */
void (*put_super) (struct super_block *); /* 卸载文件系统时由VFS调用,用来释放超级块 */
void (*write_super) (struct super_block *); /* 用给定的超级块更新磁盘上的超级块 */
int (*sync_fs)(struct super_block *sb, int wait); /* 使文件系统中的数据与磁盘上的数据同步 */
int (*statfs) (struct dentry *, struct kstatfs *); /* VFS调用该函数获取文件系统状态 */
int (*remount_fs) (struct super_block *, int *, char *); /* 指定新的安装选项又一次安装文件系统时,VFS会调用该函数 */
void (*clear_inode) (struct inode *); /* VFS调用该函数释放索引节点,并清空包括相关数据的全部页面 */
void (*umount_begin) (struct super_block *); /* VFS调用该函数中断安装操作 */
};

2.2 索引节点

索引节点是VFS中的核心概念,它包括内核在操作文件或文件夹时须要的所有信息。

一个索引节点代表文件系统中的一个文件(这里的文件不仅是指我们平时所觉得的普通的文件,还包含文件夹,特殊设备文件等等)。

索引节点和超级块一样是实际存储在磁盘上的,当被应用程序訪问到时才会在内存中创建。

索引节点定义在:<linux/fs.h>

/*
* 索引节点结构中定义的字段许多。
* 这里仅仅介绍一些重要的属性
*/
struct inode {
struct hlist_node i_hash; /* 散列表,用于高速查找inode */
struct list_head i_list; /* 索引节点链表 */
struct list_head i_sb_list; /* 超级块链表超级块 */
struct list_head i_dentry; /* 文件夹项链表 */
unsigned long i_ino; /* 节点号 */
atomic_t i_count; /* 引用计数 */
unsigned int i_nlink; /* 硬链接数 */
uid_t i_uid; /* 使用者id */
gid_t i_gid; /* 使用组id */
struct timespec i_atime; /* 最后訪问时间 */
struct timespec i_mtime; /* 最后改动时间 */
struct timespec i_ctime; /* 最后改变时间 */
const struct inode_operations *i_op; /* 索引节点操作函数 */
const struct file_operations *i_fop; /* 缺省的索引节点操作 */
struct super_block *i_sb; /* 相关的超级块 */
struct address_space *i_mapping; /* 相关的地址映射 */
struct address_space i_data; /* 设备地址映射 */
unsigned int i_flags; /* 文件系统标志 */
void *i_private; /* fs 私有指针 */
}; /*
* 当中的 i_op 中定义了索引节点的操作方法
* 这里仅仅介绍一些相对重要的函数
*/
struct inode_operations {
/* 为dentry对象创造一个新的索引节点 */
int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
/* 在特定文件夹中寻找索引节点,该索引节点要相应于dentry中给出的文件名称 */
struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
/* 创建硬链接 */
int (*link) (struct dentry *,struct inode *,struct dentry *);
/* 从一个符号链接查找它指向的索引节点 */
void * (*follow_link) (struct dentry *, struct nameidata *);
/* 在 follow_link调用之后,该函数由VFS调用进行清除工作 */
void (*put_link) (struct dentry *, struct nameidata *, void *);
/* 该函数由VFS调用,用于改动文件的大小 */
void (*truncate) (struct inode *);
};

2.3 文件夹项

和超级块和索引节点不同,文件夹项并非实际存在于磁盘上的。

在使用的时候在内存中创建文件夹项对象。事实上通过索引节点已经能够定位到指定的文件。

可是索引节点对象的属性许多。在查找,比較文件时,直接用索引节点效率不高,所以引入了文件夹项的概念。

这里能够看做又引入了一个抽象层。文件夹项是对索引节点的抽象。。!

路径中的每一个部分都是一个文件夹项,比方路径: /mnt/cdrom/foo/bar 当中包括5个文件夹项。/ mnt cdrom foo bar

每一个文件夹项对象都有3种状态:被使用,未使用和负状态

- 被使用:相应一个有效的索引节点。而且该对象由一个或多个使用者

- 未使用:相应一个有效的索引节点。可是VFS当前并没有使用这个文件夹项

- 负状态:没有相应的有效索引节点(可能索引节点被删除或者路径不存在了)

文件夹项的目的就是提高文件查找,比較的效率。所以訪问过的文件夹项都会缓存在slab中。

slab中缓存的名称一般就是 dentry,能够通过例如以下命令查看:

[wangyubin@localhost kernel]$ sudo cat /proc/slabinfo | grep dentry
dentry 212545 212625 192 21 1 : tunables 0 0 0 : slabdata 10125 10125 0

文件夹项定义在:<linux/dcache.h>

/* 文件夹项对象结构 */
struct dentry {
atomic_t d_count; /* 使用计数 */
unsigned int d_flags; /* 文件夹项标识 */
spinlock_t d_lock; /* 单文件夹项锁 */
int d_mounted; /* 是否登录点的文件夹项 */
struct inode *d_inode; /* 相关联的索引节点 */
struct hlist_node d_hash; /* 散列表 */
struct dentry *d_parent; /* 父文件夹的文件夹项对象 */
struct qstr d_name; /* 文件夹项名称 */
struct list_head d_lru; /* 未使用的链表 */
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list */
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* 子文件夹链表 */
struct list_head d_alias; /* 索引节点别名链表 */
unsigned long d_time; /* 重置时间 */
const struct dentry_operations *d_op; /* 文件夹项操作相关函数 */
struct super_block *d_sb; /* 文件的超级块 */
void *d_fsdata; /* 文件系统特有数据 */ unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* 短文件名称 */
}; /* 文件夹项相关操作函数 */
struct dentry_operations {
/* 该函数推断文件夹项对象是否有效。VFS准备从dcache中使用一个文件夹项时会调用这个函数 */
int (*d_revalidate)(struct dentry *, struct nameidata *);
/* 为文件夹项对象生成hash值 */
int (*d_hash) (struct dentry *, struct qstr *);
/* 比較 qstr 类型的2个文件名称 */
int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
/* 当文件夹项对象的 d_count 为0时,VFS调用这个函数 */
int (*d_delete)(struct dentry *);
/* 当文件夹项对象将要被释放时,VFS调用该函数 */
void (*d_release)(struct dentry *);
/* 当文件夹项对象丢失其索引节点时(也就是磁盘索引节点被删除了)。VFS会调用该函数 */
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
};

 2.4 文件对象

文件对象表示进程已打开的文件,从用户角度来看。我们在代码中操作的就是一个文件对象。

文件对象反过来指向一个文件夹项对象(文件夹项反过来指向一个索引节点)

事实上仅仅有文件夹项对象才表示一个已打开的实际文件,尽管一个文件相应的文件对象不是唯一的,但其相应的索引节点和文件夹项对象却是唯一的。

文件对象的定义在: <linux/fs.h>

/*
* 文件对象结构中定义的字段许多,
* 这里仅仅介绍一些重要的属性
*/
struct file {
union {
struct list_head fu_list; /* 文件对象链表 */
struct rcu_head fu_rcuhead; /* 释放之后的RCU链表 */
} f_u;
struct path f_path; /* 包括的文件夹项 */
const struct file_operations *f_op; /* 文件操作函数 */
atomic_long_t f_count; /* 文件对象引用计数 */
}; /*
* 当中的 f_op 中定义了文件对象的操作方法
* 这里仅仅介绍一些相对重要的函数
*/
struct file_operations {
/* 用于更新偏移量指针,由系统调用lleek()调用它 */
loff_t (*llseek) (struct file *, loff_t, int);
/* 由系统调用read()调用它 */
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
/* 由系统调用write()调用它 */
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
/* 由系统调用 aio_read() 调用它 */
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
/* 由系统调用 aio_write() 调用它 */
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
/* 将给定文件映射到指定的地址空间上,由系统调用 mmap 调用它 */
int (*mmap) (struct file *, struct vm_area_struct *);
/* 创建一个新的文件对象,并将它和对应的索引节点对象关联起来 */
int (*open) (struct inode *, struct file *);
/* 当已打开文件的引用计数降低时,VFS调用该函数 */
int (*flush) (struct file *, fl_owner_t id);
};

 2.5 四个对象之间关系图

上面分别介绍了4种对象分别的属性和方法,以下用图来展示这4个对象的和VFS之间关系以及4个对象之间的关系。

(这个图是依据我自己的理解画出来的,假设由错误请帮忙指出,谢谢!)

 3. 文件系统相关的数据结构

处理上面4个基本的对象之外,VFS中还有2个专门针对文件系统的2个对象,

- struct file_system_type: 用来描写叙述文件系统的类型(比方ext3,ntfs等等)

- struct vfsmount        : 描写叙述一个安装文件系统的实例

file_system_type 结构体位于:<linux/fs.h>

struct file_system_type {
const char *name; /* 文件系统名称 */
int fs_flags; /* 文件系统类型标志 */
/* 从磁盘中读取超级块,而且在文件系统被安装时,在内存中组装超级块对象 */
int (*get_sb) (struct file_system_type *, int,
const char *, void *, struct vfsmount *);
/* 终止訪问超级块 */
void (*kill_sb) (struct super_block *);
struct module *owner; /* 文件系统模块 */
struct file_system_type * next; /* 链表中下一个文件系统类型 */
struct list_head fs_supers; /* 超级块对象链表 */ /* 以下都是执行时的锁 */
struct lock_class_key s_lock_key;
struct lock_class_key s_umount_key; struct lock_class_key i_lock_key;
struct lock_class_key i_mutex_key;
struct lock_class_key i_mutex_dir_key;
struct lock_class_key i_alloc_sem_key;
};

每种文件系统,无论由多少个实例安装到系统中,还是根本没有安装到系统中,都仅仅有一个 file_system_type 结构。

当文件系统被实际安装时。会在安装点创建一个 vfsmount 结构体。

结构体代表文件系统的实例。也就是文件系统被安装几次,就会创建几个 vfsmount

vfsmount 的定义參见:<linux/mount.h>

struct vfsmount {
struct list_head mnt_hash; /* 散列表 */
struct vfsmount *mnt_parent; /* 父文件系统,也就是要挂载到哪个文件系统 */
struct dentry *mnt_mountpoint; /* 安装点的文件夹项 */
struct dentry *mnt_root; /* 该文件系统的根文件夹项 */
struct super_block *mnt_sb; /* 该文件系统的超级块 */
struct list_head mnt_mounts; /* 子文件系统链表 */
struct list_head mnt_child; /* 子文件系统链表 */
int mnt_flags; /* 安装标志 */
/* 4 bytes hole on 64bits arches */
const char *mnt_devname; /* 设备文件名称 e.g. /dev/dsk/hda1 */
struct list_head mnt_list; /* 描写叙述符链表 */
struct list_head mnt_expire; /* 到期链表的入口 */
struct list_head mnt_share; /* 共享安装链表的入口 */
struct list_head mnt_slave_list;/* 从安装链表 */
struct list_head mnt_slave; /* 从安装链表的入口 */
struct vfsmount *mnt_master; /* 从安装链表的主人 */
struct mnt_namespace *mnt_ns; /* 相关的命名空间 */
int mnt_id; /* 安装标识符 */
int mnt_group_id; /* 组标识符 */
/*
* We put mnt_count & mnt_expiry_mark at the end of struct vfsmount
* to let these frequently modified fields in a separate cache line
* (so that reads of mnt_flags wont ping-pong on SMP machines)
*/
atomic_t mnt_count; /* 使用计数 */
int mnt_expiry_mark; /* 假设标记为到期,则为 True */
int mnt_pinned; /* "钉住"进程计数 */
int mnt_ghosts; /* "镜像"引用计数 */
#ifdef CONFIG_SMP
int *mnt_writers; /* 写者引用计数 */
#else
int mnt_writers; /* 写者引用计数 */
#endif
};

4. 进程相关的数据结构

以上介绍的都是在内核角度看到的 VFS 各个结构,所以结构体中包括的属性许多。

而从进程的角度来看的话,大多数时候并不须要那么多的属性,全部VFS通过下面3个结构体和进程紧密联系在一起。

- struct files_struct  :由进程描写叙述符中的 files 文件夹项指向。全部与单个进程相关的信息(比方打开的文件和文件描写叙述符)都包括在当中。

- struct fs_struct     :由进程描写叙述符中的 fs 域指向。包括文件系统和进程相关的信息。

- struct mmt_namespace :由进程描写叙述符中的 mmt_namespace 域指向。

struct files_struct 位于:<linux/fdtable.h>

struct files_struct {
atomic_t count; /* 使用计数 */
struct fdtable *fdt; /* 指向其它fd表的指针 */
struct fdtable fdtab;/* 基 fd 表 */
spinlock_t file_lock ____cacheline_aligned_in_smp; /* 单个文件的锁 */
int next_fd; /* 缓存下一个可用的fd */
struct embedded_fd_set close_on_exec_init; /* exec()时关闭的文件描写叙述符链表 */
struct embedded_fd_set open_fds_init; /* 打开的文件描写叙述符链表 */
struct file * fd_array[NR_OPEN_DEFAULT]; /* 缺省的文件对象数组 */
};

struct fs_struct 位于:<linux/fs_struct.h>

struct fs_struct {
int users; /* 用户数目 */
rwlock_t lock; /* 保护结构体的读写锁 */
int umask; /* 掩码 */
int in_exec; /* 当前正在运行的文件 */
struct path root, pwd; /* 根文件夹路径和当前工作文件夹路径 */
};

struct mmt_namespace 位于:<linux/mmt_namespace.h>

可是在2.6内核之后似乎没有这个结构体了,而是用 struct nsproxy 来取代。

下面是 struct task_struct 结构体中关于文件系统的3个属性。

struct task_struct 的定义位于:<linux/sched.h>

/* filesystem information */
struct fs_struct *fs;
/* open file information */
struct files_struct *files;
/* namespaces */
struct nsproxy *nsproxy;

 5. 小结

VFS 统一了文件系统的实现框架,使得在linux上实现新文件系统的工作变得简单。

眼下linux内核支持60多个文件系统,支持的文件系统的详细信息,查看 内核源代码 fs 内容目录。

Linux虚拟文件系统VFS解决的更多相关文章

  1. linux虚拟文件系统vfs

    linux可以挂载不同的文件系统(EXT2,FAT,NTFS),用同一的样式呈现给用户,读写操作用起来都一样,这是怎样做到的呢? linux内核在各种不同的文件系统格式上做了一个抽象层,使得文件.目录 ...

  2. Linux虚拟文件系统–VFS简介

    http://www.embeddedlinux.org.cn/emb-linux/file-system/201712/20-7907.html 导读 Linux中可以支持多种文件系统,而且支持各种 ...

  3. 理解Linux虚拟文件系统VFS

    当前,除了linux标准的文件系统Ext2/Ext3/Ext4外,还有很多种文件系统,比如reiserfs, xfs, Windows的vfat NTFS,网络文件系统nfs 以及flash 文件系统 ...

  4. Linux虚拟文件系统(VFS)学习

    虚拟文件系统(Virtual Filesystem)也可称之为虚拟文件系统转换(Virtual Filesystem Switch),是一个内核软件层,用来处理与Unix标准文件系统相关的全部系统调用 ...

  5. 使用 /proc 文件系统来访问 linux操作系统 内核的内容 && 虚拟文件系统vfs及proc详解

    http://blog.163.com/he_junwei/blog/static/19793764620152743325659/ http://www.01yun.com/other/201304 ...

  6. Linux虚拟文件系统

    从文件 I/O 看 Linux 的虚拟文件系统 1 引言 Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等.通过使用同一套文件 I/O 系统 调用即可对 Linux ...

  7. Linux 虚拟文件系统四大对象:超级块、inode、dentry、file之间关系

    更多嵌入式原创文章,请关注公众号:一口Linux 一:文件系统 1. 什么是文件系统? 操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统. 通常文件系统是用于存储和组织文件的一 ...

  8. 虚拟文件系统(VFS)

    原文链接:http://www.orlion.ga/1008/ linux在不同的文件系统之上做了一个抽象层,使得文件.目录.读写访问等概念都成为抽象层概念,这个抽象层被称为虚拟文件系统(VFS). ...

  9. 虚拟文件系统VFS

    Linux的文件系统是由虚拟文件系统作为媒介搭建起来的,虚拟文件系统VFS(Virtual File System)是Linux内核层实现的一种架构,为用户空间提供统一的文件操作接口.它在内核内部为不 ...

随机推荐

  1. Skype无法显示登录界面

    Skype升级之后突然抽风,双击运行程序之后,输入用户名和密码的窗口都没了,截图如下(本机为Windows 7 32bit版本): 卸载重新安装,也无济于事.删除注册表中的Skype的相关信息后问题依 ...

  2. QMediaPlayer的duration问题

    遇到了一个坑QMediaPlayer::duration的坑. 这个坑是当你setMedia之后, 直接使用duration获取播放时长会得到0, 出错时候的代码片段例如以下: void MainWi ...

  3. Android之后台服务判断本应用Activity是否处于栈顶

    在Android开发中,我们经常想知道是否自己的服务处于后台运行中,因为在后台运行的服务器优先级会降低,也就极有可能会被系统给回收掉,有什么好办法呢?Google推荐我们将服务运行到前台,如何知道服务 ...

  4. must return an Iterable of arrays.(junit4)

    java.lang.Exception: TestIterator.init() must return an Iterable of arrays. at org.junit.runners.Par ...

  5. Swift - 区间运算符(... 和 ..<)

    区间运算符可以用来表示两个操作数之间的范围集合. 1,闭区间运算符:a...b 1 2 3 for iCount in 512...1024{      //从512遍历到1024(包括1024) } ...

  6. HTML事件

    Media 事件

  7. VC生成的DLL给QT的EXE调用时lib路径问题小结

    VC生成的DLL给QT调用,有两种方式,一种是隐式调用调用(使用.lib文件方式): ① 在*.pro工程文件中添加VC生成的lib文件路径时,或者使用一个绝对路径,如: LIBS += " ...

  8. [Android学习笔记]Unable to execute dex Multiple dex files define:xxxx 问题

    dex filse: Dalvik Execute Files , 即Android虚拟机可执行程序 从字面意思理解是你一个应用中,出现了多个Dex文件定义. 以下情况会出现此错误: 1.你项目中可能 ...

  9. easy_install MySQL-python

    python - Why can't easy_install find MySQLdb? - Stack Overflow easy_install MySQL-python

  10. Python学习入门基础教程(learning Python)--5.3 Python写文件基础

    前边我们学习了一下Python下如何读取一个文件的基本操作,学会了read和readline两个函数,本节我们学习一下Python下写文件的基本操作方法. 这里仍然是举例来说明如何写文件.例子的功能是 ...