通俗易懂!快速了解虚拟文件系统(VFS)

简介: 虚拟文件系统(VFS)

前言

为什么 Linux 内核的文件系统类型那么多,都能挂载上呢?为什么系统里可以直接 mount 其他文件系统呢?甚至能把 windows 下的文件夹挂载到 windows 上,为什么 Linux 的虚拟文件系统这么强大?这得益于它的数据结构设计得十分精妙。好像听过,Linux 有什么解决不了的?加一层。

640.png

VFS 是什么

虚拟文件系统,简称 VFS(Virtual Filesystem),是一个内核软件层。

VFS 的作用

概括地讲,VFS 有两个作用:

  1. 处理与 Unix 标准文件系统相关的所有系统调用
  2. 为各种文件系统提供一个通用的接口

VFS 支持的文件系统类型

以下列出以下常见的文件系统类型,本文暂时不对其进行详细分析。

  1. 磁盘文件系统
    ext2,ext3,···
  2. 网络文件系统类型
    nfs,smbfs,···
  3. 特殊文件系统
    tmpfs,ramfs,···
  4. 伪文件系统
    procfs,sysfs,···

VFS 的设计思想

VFS 设计的初衷就是要支持所有的文件系统,所以它的设计思想其实就是以面向对象的方式,设计一个通用的文件模型,出于效率考虑,VFS 还是 C 语言写的。

在通用文件系统模型中,每个目录也被当作一个文件,可以包含若干文件和其他的子目录。因此,Linux 有一句经典的话:一切皆文件。

关键数据结构介绍

Linux VFS 抽象出 4 种类型的数据结构,实现将不同类型的文件系统挂载到目录结构中。

超级块对象

对于磁盘类文件系统,超级块是存放在磁盘上的文件系统控制块,里面存放已安装文件系统的有关信息,换句话说,一个超级块描述了一个具体的文件系统信息,里面的信息十分重要,也叫元数据,与普通的文件数据相比,元数据丢失会损坏整个文件系统,导致无法挂载之类的问题。当然,不仅超级块,inode上也有很多元数据。

struct super_block {
  struct list_head  s_list;// 超级快链表指针
  dev_t s_dev;  // 设备表示符
  unsignedchar  s_blocksize_bits; //以位为单位的块的大小
  unsignedlong  s_blocksize; //以字节为单位的块大小
  loff_t s_maxbytes;  //文件大小的上限
  struct file_system_type *s_type;//指向文件系统的file_system_type 数据结构的指针
  conststruct super_operations  *s_op;//超级块方法
  conststruct dquot_operations  *dq_op;//磁盘限额方法
  conststruct quotactl_ops  *s_qcop;//限额控制方法
  conststruct export_operations *s_export_op;//导出方法
  unsignedlong  s_flags;  //登录标志
  unsignedlong  s_magic; //文件系统的魔术字
  struct dentry *s_root;//目录登录点
  struct rw_semaphore s_umount;//卸载信号量
  int s_count; //超级块引用计数
  atomic_t s_active; //活动引用记数
  #ifdef CONFIG_SECURITY
  void *s_security; //安全模块
  #endif
  conststruct xattr_handler **s_xattr;
  struct list_head s_inodes;//把所有索引对象链接在一起,存放的是头结点
  struct hlist_bl_head s_anon;//匿名目录项
  struct list_head s_mounts;/* list of mounts; _not_ for fs use */
  struct block_device *s_bdev;//相关的块设备
  struct backing_dev_info *s_bdi;
  struct mtd_info *s_mtd;
  struct hlist_node s_instances;//该类型文件系统
  unsignedint   s_quota_types;  /* Bitmask of supported quota types */
  struct quota_info s_dquot;//限额相关选项
  struct sb_writers s_writers;
  char s_id[32]; /* Informational name */
  u8 s_uuid[16]; /* UUID */
  void *s_fs_info; /* Filesystem private info */
  unsignedint s_max_links;
  fmode_t s_mode;
  u32 s_time_gran;
  struct mutex s_vfs_rename_mutex;/* Kludge */
  char *s_subtype;
  char __rcu *s_options;
  conststruct dentry_operations *s_d_op;/* default d_op for dentries */
  int cleancache_poolid;
  struct shrinker s_shrink;/* per-sb shrinker handle */
  atomic_long_t s_remove_count;
  int s_readonly_remount;
  struct workqueue_struct *s_dio_done_wq;
  struct hlist_head s_pins;
  struct list_lru s_dentry_lru ____cacheline_aligned_in_smp;
  struct list_lru s_inode_lru ____cacheline_aligned_in_smp;
  struct rcu_head rcu;
  int s_stack_depth;
};

索引节点对象(inode)

索引节点存放关于具体文件的一般信息。对于磁盘类文件系统,索引节点也是存放在磁盘上的文件控制块。每个索引节点都有一个索引节点号,这个节点号唯一地标识了文件系统中的文件。

struct inode {
 umode_t i_mode; //访问权限控制
 unsigned short i_opflags;
 kuid_t i_uid; //使用者的id
 kgid_t i_gid; //使用组id
 unsignedint i_flags; //文件系统标志
#ifdef CONFIG_FS_POSIX_ACL
 struct posix_acl *i_acl;
 struct posix_acl *i_default_acl;
#endif
 conststruct inode_operations *i_op;//指向索引结点操作结构体的指针
 struct super_block *i_sb;//指向inode所属文件系统的超级块的指针
 struct address_space *i_mapping;//相关的地址映射
#ifdef CONFIG_SECURITY
 void *i_security; //安全模块
#endif
 unsignedlong i_ino; //索引结点号。通过ls -i命令可以查看文件的索引节点号
 union {
  constunsignedint i_nlink; //硬链接数
  unsignedint __i_nlink;
 };
dev_t i_rdev; //实际设备标识符号
loff_t i_size; //以字节为单位
struct timespec i_atime;//最后访问时间
struct timespec i_mtime;//最后修改时间
struct timespec i_ctime;//最后改变时间
spinlock_t i_lock;  /* i_blocks, i_bytes, maybe i_size */
unsigned short i_bytes; //使用的字节数
unsignedint i_blkbits; 以位为单位的块大小
blkcnt_t i_blocks; //文件的块数
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
unsignedlong i_state; //状态标志
struct mutex i_mutex;
unsignedlong dirtied_when;  //首次修改时间
unsignedlong dirtied_time_when;
struct hlist_node i_hash;//散列表
struct list_head i_wb_list;/* backing dev IO list */
struct list_head i_lru;/* inode LRU list */
struct list_head i_sb_list;//链接一个文件系统中所有inode的链表
union {
 struct hlist_head i_dentry;//目录项链表
 struct rcu_head  i_rcu;
};
u64 i_version; //版本号
atomic_t i_count; //引用计数
atomic_t i_dio_count;
atomic_t i_writecount; //写者计数
#ifdef CONFIG_IMA
atomic_t i_readcount; /* struct files open RO */
#endif
conststruct file_operations *i_fop;/* former ->i_op->default_file_ops */
struct file_lock_context *i_flctx;
struct address_space i_data;//设备地址映射
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_marks;
#endif
void *i_private; /* fs or device private pointer */
};

目录项对象(dentry)

存放 dentry 与对应文件链接的有关信息,每个 dentry 代表路径中的一个特定部分,每个磁盘类文件系统以自己的方式将目录项信息存放在磁盘上。

struct dentry {
 /* RCU lookup touched fields */
unsignedint d_flags; /* protected by d_lock */
seqcount_t d_seq; /* per dentry seqlock */
struct hlist_bl_node d_hash;/* lookup hash list */
struct dentry *d_parent;/* parent directory */
struct qstr d_name;
struct inode *d_inode;/* Where the name belongs to - NULL is negative */
unsignedchar d_iname[DNAME_INLINE_LEN]; /* small names */
/* Ref lookup also touches following */
struct lockref d_lockref;/* per-dentry lock and refcount */
conststruct dentry_operations *d_op;
struct super_block *d_sb;/* The root of the dentry tree */
unsignedlong d_time; /* used by d_revalidate */
void *d_fsdata; /* fs-specific data */
struct list_head d_lru;/* LRU list */
struct list_head d_child;/* child of parent list */
struct list_head d_subdirs;/* our children */
/*
 * d_alias and d_rcu can share memory
 */
union {
 struct hlist_node d_alias;/* inode alias list */
 struct rcu_head d_rcu;
} d_u;
};

文件对象(file)

存放被打开文件与进程间交互的信息,这类信息仅当进程访问文件期间存放在内存中。

struct file {
 union {
  struct llist_node fu_llist;//每个文件系统中被打开的文件都会形成一个双链表
  struct rcu_head  fu_rcuhead;
 } f_u;
struct path f_path;
struct inode *f_inode;/* cached value */
conststruct file_operations *f_op;//指向文件操作表的指针
spinlock_t f_lock;
atomic_long_t f_count; //文件对象的使用计数
unsignedint  f_flags; //打开文件时所指定的标志
fmode_t f_mode; //文件的访问模式
struct mutex f_pos_lock;
loff_t f_pos; //文件当前的位移量
struct fown_struct f_owner;
conststruct 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;
 struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space  *f_mapping;//页缓存映射
} __attribute__((aligned(4)));  /* lest something weird decides that 2 is OK */
struct file_handle {
__u32 handle_bytes;
int handle_type;
/* file identifier */
unsignedchar f_handle[0];
};

数据结构组织关系图

640.png

号主:一枚机械专业本科生,经历了转行,从外包逆袭到芯片原厂的Linux驱动开发工程师,深入操作系统的世界,贯彻终身学习、终身成长的理念。平时喜欢折腾,寒冬之下,抱团取暖,期待你来一起探讨技术、搞自媒体副业,程序员接单和投资理财。【对了,不定期送闲置开发板、书籍、键盘等等】。

如果你想了解我的转行经验,欢迎找我交流~gongzhong号【哆哆jarvis】

一起不断探索自我、走出迷茫、找到热爱,希望和你成为朋友,一起成长~

相关文章
|
4月前
|
存储 缓存 Linux
Linux源码阅读笔记15-块设备详解
Linux源码阅读笔记15-块设备详解
|
4月前
|
存储 Linux 数据库
Linux源码阅读笔记16-文件系统关联及字符设备操作
Linux源码阅读笔记16-文件系统关联及字符设备操作
|
3月前
|
存储 安全 Linux
sysfs - 用于导出内核对象的_文件系统 【ChatGPT】
sysfs - 用于导出内核对象的_文件系统 【ChatGPT】
|
存储 Linux 文件存储
linux系统编程(六) linux文件系统的操作(上)
linux系统编程(六) linux文件系统的操作
282 0
linux系统编程(六) linux文件系统的操作(上)
|
Linux 开发工具
linux系统编程(六) linux文件系统的操作(下)
linux系统编程(六) linux文件系统的操作
131 0
linux系统编程(六) linux文件系统的操作(下)
|
运维 Linux 应用服务中间件
Linux高薪必备-软硬链接及文件系统inode| 学习笔记
快速学习Linux高薪必备-软硬链接及文件系统inode
131 0
 Linux高薪必备-软硬链接及文件系统inode| 学习笔记
|
存储 传感器 固态存储
3.2 Linux文件系统到底有什么用处?
Linux 上的文件系统一般来说就是EXT2或EXT3,但这篇文章并不准备一上来就直接讲它们,而希望结合Linux操作系统并从文件系统建立的基础——硬盘开始,一步步认识Linux的文件系统。
458 0
3.2 Linux文件系统到底有什么用处?
|
Linux API
Linux驱动开发: 杂项字符设备
Linux驱动开发: 杂项字符设备
244 0
Linux驱动开发: 杂项字符设备
|
存储 缓存 算法
Linux驱动开发: 块设备驱动开发
Linux驱动开发: 块设备驱动开发
431 0
Linux驱动开发: 块设备驱动开发