VFS

概念

虚拟文件系统(也称为虚拟文件系统交换机)是内核中的软件层,为用户空间程序提供文件系统接口。它还在内核中提供了一个抽象,允许不同的文件系统实现共存

"一切皆文件"是Linux的基本哲学之一,不仅是普通的文件,包括目录、字符设备、块设备、套接字等,都可以以文件的方式被对待。实现这一行为的基础,正是Linux的虚拟文件系统机制。。

还记得系统调用吧?你可以VFS类似给各种文件系统抽象出来了一个系统调用,把各种类型的文件系统封装成对上层不可见的,同时留出一些api。示意图如下

VFS设计

Linux为了实现这种VFS系统,采用面向对象的设计思路,主要抽象了四种对象类型:

  • 超级块对象:代表一个已安装的文件系统。 (struct super_block)
  • 索引节点对象:代表具体的文件。 (struct inode)
  • 目录项对象:代表一个目录项,是文件路径的一个组成部分。 (struct denrtry)
  • 文件对象:代表进程打开的文件。 (struct file)

容易混淆的地方

Linux将目录当做文件对象来处理,是另一种形式的文件,它里面包含了一个或多个目录项。而目录项是单独抽象的对象,主要包括文件名和索引节点号。因为目录是可以层层嵌套,以形成文件路径,而路径中的每一部分,其实就是目录项

struct super_block (代表一个已安装的文件系统)

include/linux/fs.h

超级块代表的是一种文件系统类型,比如ext3、ext4都有对应的super_block结构体。一台机器可以有多块硬盘,一个硬盘可以有多个分区,每个分区都有自己的文件系统类型,超级块同时也维护跟这个文件系统有关的各种信息。 各个super_block由链表组织

struct super_block {struct list_head s_list; //通过该变量链接到超级块全局链表super_blocks上dev_t         s_dev;      //该文件系统对应的块设备标识符unsigned char       s_blocksize_bits;unsigned long      s_blocksize; //该文件系统的block sizeloff_t           s_maxbytes; //文件系统支持的最大文件struct file_system_type    *s_type; //文件系统类型,比如ext3、ext4const struct super_operations   *s_op; //超级块的操作函数const struct dquot_operations  *dq_op; //文件系统限额相关操作const struct quotactl_ops   *s_qcop; //磁盘限额const struct export_operations *s_export_op;unsigned long        s_flags; //文件系统的mount标记unsigned long        s_iflags;   /* internal SB_I_* flags */unsigned long        s_magic;  //该文件系统类型的魔术字struct dentry        *s_root; //全局根目录的dentry项...struct block_device  *s_bdev;  //对应的块设备struct backing_dev_info *s_bdi; //超级块对应的BDI设备struct mtd_info      *s_mtd;//通过该变量,链接到file_system_type中的fs_supers链表struct hlist_node s_instances;...char         s_id[32];   /* Informational name */uuid_t          s_uuid;     /* UUID tune2fs -l可以查看*/void            *s_fs_info; //指向具体文件系统超级块结构,如ext4_sb_info...const struct dentry_operations *s_d_op; //该超级块默认的目录项操作函数...struct shrinker s_shrink; //每个超级块注册的shrink函数,用于内存回收.../* AIO completions deferred from interrupt context */struct workqueue_struct *s_dio_done_wq;...//该超级块对应的未在使用dentry列表struct list_lru      s_dentry_lru ____cacheline_aligned_in_smp;//该超级块对应的未在使用inode列表struct list_lru       s_inode_lru ____cacheline_aligned_in_smp;.../* s_inode_list_lock protects s_inodes */spinlock_t     s_inode_list_lock ____cacheline_aligned_in_smp;struct list_head s_inodes;   //该超级块包含的所有inodespinlock_t      s_inode_wblist_lock;struct list_head    s_inodes_wb;    //该超级块正在回写的inode
};

ps:超级块的信息是可读的!通过cat 某个文件可以human readab。具体是哪个我忘了。。。在油管一个视频上看到的

const struct dentry_operations *s_d_op 存放着文件系统的目录项操作函数(函数指针)

struct file_system_type

超级块是实装的文件系统,file_system_type专注于描述这个这个文件系统的类型,由链表组织

每种文件系统都要把自己的信息挂到super_blocks这么一个全局链表上

file_system_type挂载到super_block

内核中是分成2个步骤完成:

首先每个文件系统必须通过register_filesystem函数将自己的file_system_type挂接到file_systems这个全局变量上,

然后调用kern_mount函数把自己的文件相关操作函数集合表挂到super_blocks上。每种文件系统类型的读超级块的例程(get_sb)必须由自己实现

struct file_system_type {const char *name; //文件系统名称,如ext4,xfs...struct dentry *(*mount) (struct file_system_type *, int,const char *, void *); //对应的mount函数void (*kill_sb) (struct super_block *);struct module *owner;struct file_system_type * next; //通过该变量将系统上所有文件系统类型链接到一起struct hlist_head fs_supers; //该文件系统类型锁包含的超级块对象...
};

struct inode (物理Device storage的具体对象)

当创建一个文件的时候,就给文件分配了一个inode。一个inode只对应一个实际文件,一个文件也会只有一个inode。inodes最大数量就是文件的最大数量。

inode结构中的静态信息取自物理设备上的文件系统,由文件系统指定的函数填写,它只存在于内存中,可以通过inode缓存访问。

struct inode {struct list_headi_hash;struct list_headi_list;struct list_headi_dentry;struct list_headi_dirty_buffers;unsigned longi_ino; /*每一个inode都有一个序号,经由super block结构和其序号,我们可以很轻易的找到这个inode。*/atomic_t i_count; /*在Kernel里,很多的结构都会记录其reference count,以确保如果某个结构正在使用,它不会被不小心释放掉,i_count就是其reference count。*/kdev_t i_dev; /* inode所在的device代码 */umode_t i_mode; /* inode的权限 */nlink_t i_nlink; /* hard link的个数 */uid_t i_uid; /* inode拥有者的id */gid_t i_gid; /* inode所属的群组id */kdev_t i_rdev; /* 如果inode代表的是device的话,那此字段将记录device的代码 */ off_t i_size; /* inode所代表的档案大小 */time_t i_atime; /* inode最近一次的存取时间 */time_t i_mtime; /* inode最近一次的修改时间 */time_t i_ctime; /* inode的产生时间 */ unsigned long i_blksize; /* inode在做IO时的区块大小 */unsigned long i_blocks; /* inode所使用的block数,一个block为512 byte*/unsigned long i_version; /* 版本号码 */ unsigned short i_bytes;struct semaphore i_sem;struct rw_semaphore i_truncate_sem;struct semaphore i_zombie;struct inode_operations *i_op;struct file_operations *i_fop;/* former ->i_op->default_file_ops */struct super_block *i_sb; /* inode所属档案系统的super block */wait_queue_head_t i_wait;struct file_lock *i_flock; /* 用来做file lock */struct address_space *i_mapping;struct address_space i_data;struct dquot *i_dquot [MAXQUOTAS];/* These three should probably be a union */struct pipe_inode_info *i_pipe;struct block_device *i_bdev;struct char_device *i_cdev;unsigned longi_dnotify_mask; /* Directory notify events */struct dnotify_struct *i_dnotify; /* for directory notifications */unsigned long i_state; /* inode目前的状态,可以是I_DIRTY,I_LOCK和 I_FREEING的OR组合 */ unsigned int i_flags; /* 记录此inode的参数 */ unsigned char i_sock; /* 用来记录此inode是否为socket */ atomic_t i_write count;unsigned int i_attr_flags; /* 用来记录此inode的属性参数 */ __u32 i_generation;union {struct minix_inode_info minix_i;struct ext2_inode_info ext2_i;struct ext3_inode_info ext3_i;struct hpfs_inode_info hpfs_i;struct ntfs_inode_info ntfs_i;struct msdos_inode_info msdos_i;struct umsdos_inode_info umsdos_i;struct iso_inode_info isofs_i;struct sysv_inode_info sysv_i;struct affs_inode_info affs_i;struct ufs_inode_info ufs_i;struct efs_inode_info efs_i;struct romfs_inode_info romfs_i;struct shmem_inode_info shmem_i;struct coda_inode_info coda_i;struct smb_inode_info smbfs_i;struct hfs_inode_info hfs_i;struct adfs_inode_info adfs_i;struct qnx4_inode_info qnx4_i;struct reiserfs_inode_info reiserfs_i;struct bfs_inode_info bfs_i;struct udf_inode_info udf_i;struct ncp_inode_info ncpfs_i;struct proc_inode_info proc_i;struct socketsocket_i;struct usbdev_inode_info usbdev_i;struct jffs2_inode_infojffs2_i;void *generic_ip;} u;
};

虽然每个文件都有相应的inode结点,但是只有在需要的时候系统才会在内存中为其建立相应的inode数据结构,建立的inode结构将形成一个链表,我们可以通过遍历这个链表去得到我们需要的文件结点,

struct dentry (主存中的一个实体)

为什么引入dentry

引入目录项结构是很有必要的,因为同一个文件有且仅有一个inode对象表示,而由于硬链接的存在,对同一文件的访问可以通过不同的文件名,所以中间需要引入目录项

struct dentry {atomic_t d_count;             /* 目录项对象引用计数器 */unsigned int d_flags;           /* 目录项高速缓存标志 */spinlock_t d_lock;               /* 保护目录项对象的自旋锁 */struct inode *d_inode;         /* 与文件名关联的索引节点 */struct hlist_node d_hash;      /* 指向散列表表项链表的指针 */struct dentry *d_parent;      /* 父目录的目录项对象 */struct qstr d_name;              /* 文件名 */struct list_head d_lru;            /* 用于未使用目录项链表的指针 */union {struct list_head d_child; /* 对目录而言,用于同一父目录中的目录项链表的指针 */struct rcu_head d_rcu;      /* 回收目录项对象时,由RCU描述符使用 */} d_u;struct list_head d_subdirs;        /* 对目录而言,子目录项链表的头 */struct list_head d_alias;        /* 用于与同一索引节点(别名)相关的目录项链表的指针 */unsigned long d_time;           /* 由d_revalidate方法使用 */struct dentry_operations *d_op;  /* 目录项方法 */struct super_block *d_sb;        /* 文件的超级块对象 */void *d_fsdata;                   /* 依赖于文件系统的数据 */struct dcookie_struct *d_cookie;/* 指向内核配置文件使用的数据结构的指针 */int d_mounted;                  /* 对目录项而言,用于记录安装该目录项的文件系统数的计数器 */unsigned char d_iname[DNAME_INLINE_LEN_MIN];    /* 存放短文件名的空间 */
};

目录项的三种状态

1.正在使用 d_count > 0

2.未被使用 d_count == 0

3.负状态 d_inode == NULL 目录项对象没有对应的有效索引节点

struct file (被打开的文件对象)

include/Linux/fs.h

文件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的 struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。在内核创建和驱动源码中,struct file的指针通常被命名为file或filp

struct file {union {struct list_head fu_list; //文件对象链表指针linux/include/linux/list.hstruct rcu_head fu_rcuhead; //RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制} f_u;struct path f_path; //包含dentry和mnt两个成员,用于确定文件路径#define f_dentry f_path.dentry //f_path的成员之一,当前文件的dentry结构#define f_vfsmnt f_path.mnt //表示当前文件所在文件系统的挂载根目录const struct file_operations *f_op; //与该文件相关联的操作函数atomic_t f_count; //文件的引用计数(有多少进程打开该文件)unsigned int f_flags; //对应于open时指定的flagmode_t f_mode; //读写模式:open的mod_t mode参数loff_t     f_pos;//当前文件指针位置off_t f_pos; //该文件在当前进程中的文件偏移量struct fown_struct f_owner; //该结构的作用是通过信号进行I/O时间通知的数据。unsigned int f_uid, f_gid;// 文件所有者id,所有者组idstruct file_ra_state f_ra; //在linux/include/linux/fs.h中定义,文件预读相关unsigned long f_version;//记录文件的版本号,每次使用之后递增#ifdef CONFIG_SECURITYvoid *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;spinlock_t f_ep_lock;#endif /* #ifdef CONFIG_EPOLL */struct address_space *f_mapping;};

dentry与inode

inode(可理解为ext2 inode)对应于物理磁盘上的具体对象,dentry是一个内存实体,其中的d_inode成员指向对应的inode。也就是说,一个inode可以在运行的时候链接多个dentry,而d_count记录了这个链接的数量

按照d_count的值,dentry分为以下三种状态:

1.正在使用 d_count > 0

2.未被使用 d_count == 0

3.负状态 d_inode == NULL 目录项对象没有对应的有效索引节点

File_system_type -->file_system —>super_block关系图

VFS中各个重要数据结构关系图

【操作系统】LinuxKernel-VFS虚拟文件系统 认知框架构建(super_block,inode,dentry,file)相关推荐

  1. linux VFS 虚拟文件系统 简介 super_block inode dentry file

    1.简介 1.Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等.通过使用同一套文件 I/O 系统 调用即可对 Linux 中的任意文件进行操作而无需考虑其所在的具体文 ...

  2. Linux(一) VFS虚拟文件系统

    一.先了解一下什么是挂载 Linux有自己的一套文件系统,例如Ext2.Ext3,但是外部其他文件系统时,由于各个文件系统都各自有一套的文件管理体系,是无法通过Linux本身访问文件的方式直接访问的, ...

  3. VFS - 虚拟文件系统的加载和导出

    VFS - 代码生成器预览功能实现 VFS - 虚拟文件系统基本操作方法的封装 VFS - 虚拟文件系统的加载和导出 这是 VFS 的最后一篇,前面两篇中的基本方法已经实现了一个简单的虚拟文件系统,可 ...

  4. VFS - 虚拟文件系统基本操作方法的封装

    VFS - 代码生成器预览功能实现 VFS - 虚拟文件系统基本操作方法的封装 VFS - 虚拟文件系统的加载和导出 接前一篇 VFS - 代码生成器预览功能实现 ,上一篇讲到了 mkdirs 封装创 ...

  5. Linux 字符设备驱动开发基础(六)—— VFS 虚拟文件系统解析

    一.VFS 虚拟文件系统基础概念 Linux 允许众多不同的文件系统共存,并支持跨文件系统的文件操作,这是因为有虚拟文件系统的存在.虚拟文件系统,即VFS(Virtual File System)是 ...

  6. Linux·VFS虚拟文件系统

    目录 1 概念 2 架构 3 接口适配示例 4 跨设备/文件系统示例 5 VFS的抽象接口 6 Linux系统VFS支持的文件系统 7 统一文件模型(common file model) 7.1 Su ...

  7. 【文件系统】VFS虚拟文件系统

    1.VFS Linux支持各种各样的文件系统格式,比如说ext2,ext3,reiserfs,FAT,NTFS,iso9660等, 不同的磁盘分区,光盘或者其他存储设备都有不同的文件系统格式,然后这些 ...

  8. 11Linux服务器编程之:VFS虚拟文件系统,dup()函数和dup2()函数

     1dup函数和dup2函数 #include<unistd.h> int dup(intoldfd); int dup2(intoldfd, int newfd); dup和dup2 ...

  9. linux文件系统dentry_Linux 文件系统(一)---虚拟文件系统VFS----超级块、inode、dentry、file...

    一: 什么是文件系统,详见:http://zh.wikipedia.org/zh/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F 其实一句话就是管理这块文件的机制(组织方式, ...

最新文章

  1. Samba服务器搭建案例
  2. PostgreSQL可视化客户端工具
  3. ViewPager 在 ScrollView 中显示不全的解决方法
  4. 根据控件句柄读控件在内存的数据_WPF 2020界面开发新纪元——Accordion控件、图表功能升级...
  5. C++11学习笔记-----互斥量以及条件变量的使用
  6. Android Studio下载及安装3.0版本
  7. Qt编程'hello world
  8. Java重载和重写6_深入理解Java中的重写和重载
  9. 华为交换机s2700怎么重置_华为交换机忘记console的密码,怎么恢复出厂设置
  10. redis linux 运行依赖,在Ubuntu系统下安装Redis Desktop Manager的方法
  11. linux下git的使用
  12. apache commons lang架包介绍
  13. initrd的启动过程
  14. 热电偶校验仪使用说明_APSL311系列压力校验仪
  15. app error login.php,如何解决uniapp登录错误提示问题
  16. Configuring SharePoint 2010 and ADFS v2 End to End-摘自网络
  17. 联想ts250进bios_联想Think TS250完美安装server 2008 R2 | 涂涂小窝
  18. 2019年中国航空三字代码对照表完全汇总
  19. Python实现对网易云音乐的数据进行一个数据清洗和可视化分析
  20. 计算机桌面移动如何解决,【电脑小知识】桌面布局被锁定,图标不能随意移动了怎么办?...

热门文章

  1. linux下的buff/cache内存占用过高-手动清除释放内存
  2. 技术总监岗位职责,网站技术总监的职能
  3. java nio rewind_java.nio.ByteBuffer 以及flip,clear及rewind区别
  4. 男人的最高品位在于选择女人
  5. 华为MPLS L2xxx(PWE3)+静动混合多跳PW实验
  6. Linux木马清理记录
  7. 【电气专业知识问答】问:电力系统稳定器PSS的定义和功能是什么?投用时应注意哪些问题?
  8. topcoder入门
  9. css3自定义字体实现毛体输出沁园春雪以及font属性
  10. 网页突然报502 bad gateway,平台宕掉