虚拟文件系统(Virtual Filesystem)也可称之为虚拟文件系统转换(Virtual Filesystem Switch),是一个内核软件层,用来处理与Unix标准文件系统相关的全部系统调用。其健壮性表如今能为各种文件系统提供一个通用的接口。

通用文件系统模型

VFS所隐含的主要思想在于引入一个通用的文件系统模型(common file model),这个模型可以表示全部支持的文件系统。在通用文件模型中,每一个文件夹被看做一个文件,可以包括若干文件和其它的子文件夹。

通用文件模型由下列对象类型组成:

超级块对象(superblock object)

存放已安装文件系统的有关信息(A superblock object represents a mounted filesystem)。对基于磁盘的文件系统,这类对象通常相应于存放在磁盘上的文件系统控制块

索引节点对象(inode object)

存放关于详细文件的一般信息(An inode object represents an object within the filesystem)。对于基于磁盘的文件系统,这类对象通常相应于存放在磁盘上的文件控制块。每一个索引节点对象都有一个索引节点号,这个节点号唯一地标识文件系统中的文件。

文件对象(file object)

存放打开文件与进程之间进行交互的有关信息(A file object represents a file opened by a process)。这类信息仅当进程訪问文件期间存在于内核内存中。

文件夹项对象(dentry object)

存放文件夹项(也就是文件的特定名称)与相应文件进行链接的有关信息。

VFS的数据结构

这里仅仅列举和进程相关的结构

索引节点对象

文件系统处理文件所须要的全部信息都放在一个名为索引节点的数据结构中。文件名称能够随时更改,可是索引节点对文件是唯一的,而且随着文件的存在而存在。内存中索引节点对象由一个struct inode数据结构构成。

struct inode {
struct hlist_node i_hash; //用于散列链表
struct list_head i_list; /* backing dev IO 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; //全部者标识符
gid_t i_gid; //组标识符
dev_t i_rdev; //实设备标识符
u64 i_version; //版本(每次使用后递增)
loff_t i_size; //文件的字节数
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
struct timespec i_atime; //上次訪问文件的时间
struct timespec i_mtime; //上次写文件的时间
struct timespec i_ctime; //上次改动索引节点的时间
blkcnt_t i_blocks; //文件的块数
unsigned int i_blkbits; //块的位数
unsigned short i_bytes;//块的字节数
umode_t i_mode; //文件的类型和訪问权限
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
struct mutex i_mutex;
struct rw_semaphore i_alloc_sem; //在直接I/O文件操作中避免出现竞争条件的读写信号量
const struct inode_operations *i_op; //索引节点的操作
const struct file_operations *i_fop; /*缺省文件操作former ->i_op->default_file_ops */
struct super_block *i_sb; //指向超级块的指针
struct file_lock *i_flock;
struct address_space *i_mapping; //指向address_space对象的指针
struct address_space i_data; //文件的address_space对象
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS]; //索引节点磁盘限额
#endif
struct list_head i_devices; //用于详细的字符或块设备索引节点链表
union {
struct pipe_inode_info *i_pipe; //假设文件是个管道则使用它
struct block_device *i_bdev; //指向块设备驱程序的指针
struct cdev *i_cdev; //指向字符设备驱动程序的指针
}; __u32 i_generation; //索引节点的版本 #ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct hlist_head i_fsnotify_mark_entries; /* fsnotify mark entries */
#endif #ifdef CONFIG_INOTIFY
struct list_head inotify_watches; /* watches on this inode */
struct mutex inotify_mutex; /* protects the watches list */
#endif unsigned long i_state; //索引节点状态标志
unsigned long dirtied_when; /* jiffies of first dirtying */ unsigned int i_flags; //文件系统安装标志 atomic_t i_writecount;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
void *i_private; /* fs or device private pointer */
};

文件对象

文件对象描写叙述进程如何与一个打开的文件进行交互。文件对象是在文件被打开时创建的,由一个file结构组成。文件对象在磁盘上是没有相应的映像,因此file结构中没有设置“脏”字段来表示文件对象是否已被改动。

struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry //与文件相关的文件夹项对象
#define f_vfsmnt f_path.mnt //含有该文件的已安装文件系统
const struct file_operations *f_op; //文件操作表指针
spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */
atomic_long_t f_count; //文件对象的引用计数器
unsigned int f_flags; //当打开文件时所指定的标志
fmode_t f_mode; //进程訪问模式
loff_t f_pos; //当前的文件偏移量
struct fown_struct f_owner; //通过信号进行I/O事件通知的数据
const struct cred *f_cred;
struct file_ra_state f_ra; //文件预读状态 u64 f_version; //版本,每次使用后自己主动递增
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;//指向特定文件系统或设备驱动程序所须要数据的指针 #ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;//文件的事件轮询等待着链表头
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;//指向文件地址空间对象的指针
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};

文件对象通过一个名为filp的slab快速缓存分配,filp描写叙述符地址存放在filp_cachep变量中。因为分配的文件对象数目是有限的,因此files_stat变量在其max_files字段中指定了可分配的文件对象的最大数目,也就是系统可同一时候訪问的最大文件数。

内核初始化期间,files_init()函数把max_files字段设置为可用RAM大小的1/10。只是,系统管理员能够通过写/proc/sys/fs/file-max文件来改动这个值。并且即使max_files个文件对象已经被分配,超级用户也总是能够获得一个文件对象

void __init files_init(unsigned long mempages)
{
int n; filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); /*
* One file with associated inode and dcache is very roughly 1K.
* Per default don't use more than 10% of our memory for files.
*/ n = (mempages * (PAGE_SIZE / 1024)) / 10;
files_stat.max_files = n;
if (files_stat.max_files < NR_FILE)
files_stat.max_files = NR_FILE;
files_defer_init();
percpu_counter_init(&nr_files, 0);
}

文件夹项对象

VFS把每一个文件夹看做若干子文件夹和文件组成的一个普通文件。一旦文件夹项被读入内存,VFS就把它转换成基于dentry结构的一个文件夹项对象。对于进程查找的路径名中的每一个分量,内核都为其创建一个文件夹项对象;文件夹项对象将每一个分量与其相应的索引节点相联系。比如在查找路径名/tmp/test时,内核为根文件夹“/”创建一个文件夹项对象,为根文件夹下的tmp项创建第二级文件夹项对象,为/tmp文件夹下的test创建一个第三级文件夹项对象。

文件夹项对象在磁盘上并没有相应的映像,因此在dentry结构中不包括指出该对象已被改动的字段。文件夹项对象存放在名为dentry_cache的快速缓存中。

struct dentry {
atomic_t d_count; //文件夹项对象引用计数
unsigned int d_flags; /* 文件夹项快速缓存标志protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
int d_mounted; //对文件夹而言,用于记录安装该文件夹项的文件系统计数器
struct inode *d_inode; /* 与文件名称关联的索引节点Where the name belongs to - NULL is
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* 父文件夹的文件夹项对象parent directory */
struct qstr d_name; //文件名称 struct list_head d_lru; /* LRU list */
/*
* 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; /* our children */
struct list_head d_alias; /* inode alias list */
unsigned long d_time; /* used by d_revalidate */
const struct dentry_operations *d_op;//文件夹项方法
struct super_block *d_sb; /* 文件的超级块对象The root of the dentry tree */
void *d_fsdata; /* 依赖于文件系统的数据fs-specific data */ unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
};

与进程相关的文件

每一个进程都有它自己当前的工作文件夹和他自己的根文件夹。这不过内核用来表示进程与文件系统相互作用所必须维护的数据的两个样例。类型为fs_struct的整个数据结构就用于此目的

struct fs_struct {
int users;
rwlock_t lock;
int umask; //当打开文件设置文件权限是所用的位掩码
int in_exec;
struct path root, pwd; /* 根文件夹的文件夹项,根文件夹所安装的文件系统对象
当前工作的文件夹项,当前工作文件夹所安装的文件系统对象*/
};

进程当前打开的文件与files_struct结构有关

struct fdtable {
unsigned int max_fds; //文件对象的当前最大数目
struct file ** fd; /* current fd array */
fd_set *close_on_exec;
fd_set *open_fds;
struct rcu_head rcu;
struct fdtable *next;
}; /*
* Open file table structure
*/
struct files_struct {
/*
* read mostly part
*/
atomic_t count; //共享进程的数目
struct fdtable *fdt; //
struct fdtable fdtab;
/*
* written part on a separate cache line in SMP
*/
spinlock_t file_lock ____cacheline_aligned_in_smp;
int next_fd;
struct embedded_fd_set close_on_exec_init;
struct embedded_fd_set open_fds_init;
struct file * fd_array[NR_OPEN_DEFAULT];//文件对象指针的初始化数组
};

fd字段指向文件对象的指针数组。该数组的长度存放在max_fds字段中。通常,fd字段指向files_struct结构中的fd_array字段,该字段包含32个文件对象指针。假设进程打开的文件数据多于32,内核就分配一个新的、更大的文件指针数组,并将其地址存放在fd字段中,内核同一时候更新max_fds字段的值。

相应fd数组中有元素的每一个文件来说,数组的索引就是文件描写叙述符。通常,数组的第一个元素(索引0)是进程的标准输入文件,数组的第二个元素(索引1)是进程的标准输出文件,数组的第三个元素(索引2)是进程的标准错误输出文件。

内核在进程描写叙述符的signal->rlim[RLIM_NLIMITS]结构上强制动态限制文件描写叙述符的最大数;这个值通常为1024,但假设进程具有超级权限,就能够增大这个值。

最经常使用的特殊文件系统类型

名字           安装点     说明

bdev           无        块设备

binfmt_misc    随意      其它可运行格式

devpts         /dev/pts  伪终端支持

eventpollfs    无        由有效事件轮询机制使用

futexfs        无        由futex(高速用户态加锁)机制使用

pipefs         无        管道

proc           /proc     对内核数据结构的常规訪问点

rootfs         无        为启动阶段提供一个空的根文件夹

shm            无        IPC共享线性区

mqueue         随意       实现POSIX消息队列时使用

sockfs         无         套接字

sysfs          /sys       对系统參数的常规訪问

tmpfs          随意        暂时文件(假设不被交换出去就保持在RAM中)

usbfs          /proc/bus/usb USB设备

文件系统注冊

每一个注冊的文件系统都用一个类型为file_system_type的对象来表示

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

以sockfs文件系统注冊为例

static struct file_system_type sock_fs_type = {
.name = "sockfs",
.get_sb = sockfs_get_sb,
.kill_sb = kill_anon_super,
}; static int __init sock_init(void)
{
/*
* Initialize sock SLAB cache.
*/ sk_init(); /*
* Initialize skbuff SLAB cache
*/
skb_init(); /*
* Initialize the protocols module.
*/ init_inodecache();
/* 注冊sockfs文件系统 */
register_filesystem(&sock_fs_type);
sock_mnt = kern_mount(&sock_fs_type); /* The real protocol initialization is performed in later initcalls.
*/ #ifdef CONFIG_NETFILTER
netfilter_init();
#endif return 0;
}

Linux虚拟文件系统(VFS)学习的更多相关文章

  1. Linux虚拟文件系统VFS解决

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

  2. linux虚拟文件系统vfs

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

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

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

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

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

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

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

  6. 虚拟文件系统(VFS)

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

  7. 虚拟文件系统VFS

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

  8. Linux虚拟文件系统

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

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

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

随机推荐

  1. Windows下合并tar分卷

    如有例如以下几个tar分卷:logs.tar.gza1.logs.tar.gza2.logs.tar.gza3.在Windows下怎样进行合并呢? 按"win+r"键在弹出的输入框 ...

  2. Maven 使用Eclipse构建Maven的SpringMVC项目

    首先Eclipse需要安装Maven的插件,地址:http://m2eclipse.sonatype.org/sites/m2e. 用MyEclipse安装Maven插件,建出的Maven项目有些问题 ...

  3. memcached缓存分布式部署方案

    一.分布式方案介绍 比较流行的两种方案: 1.取余分布: 计算key的哈希值,与服务器数量取余,得到目标服务器.优点:实现简单,当某台服务器不可用时,故障转移方便:缺点:当增减服务器时, Key与服务 ...

  4. 云服务器搭建 Nginx 静态网站

    第一步:安装 Nginx 在 CentOS 上,可直接使用 yum 来安装 Nginx(当然也可以通过下载压缩包.解压.编译的方式安装,不过太麻烦了) yum install nginx -y 第二步 ...

  5. XML Parser Errors See Details for more Information XML Parser Error on line 1: Document root ele

    1.错误描写叙述 XML Parser Errors See Details for more Information XML Parser Error on line 1: Document roo ...

  6. python3的函数

    #摘自廖雪峰的程序教程 函数名是变量: 如abs()是一个求绝对值的函数, >>> x = abs(-10) >>> x 10 变量可以指向函数 用f指向函数abs ...

  7. [PReact] Create a Hello World App with Preact

    By creating a simple ‘hello world’ example application first in vanilla Javascript, and then in Prea ...

  8. [RxJS] Replace zip with combineLatest when combining sources of data

    This lesson will highlight the true purpose of the zip operator, and how uncommon its use cases are. ...

  9. 【Python排序搜索基本算法】之拓扑排序

    拓扑排序是对有向无环图的一种排序,满足例如以下两个条件: 1.每一个顶点出现且仅仅出现一次. 2.若A在序列中排在B的前面.则在图中不存在从B到A的路径. 如上的无环有向图,v表示顶点:v=['a', ...

  10. 简要分析unity3d中剪不断理还乱的yield

    在学习unity3d的时候非常easy看到以下这个样例: void Start () { StartCoroutine(Destroy()); } IEnumerator Destroy(){ yie ...