IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 【操作系统】LinuxKernel-VFS虚拟文件系统 认知框架构建(super_blockinodedentryfile) -> 正文阅读

[系统运维]【操作系统】LinuxKernel-VFS虚拟文件系统 认知框架构建(super_blockinodedentryfile)

VFS

概念

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

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

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

img

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 size
	loff_t			s_maxbytes;	//文件系统支持的最大文件
	struct file_system_type	*s_type; //文件系统类型,比如ext3、ext4
	const 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;	//该超级块包含的所有inode

	spinlock_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.h
        struct 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时指定的flag
  mode_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,所有者组id
  struct file_ra_state f_ra; //在linux/include/linux/fs.h中定义,文件预读相关
  unsigned long 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;
      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关系图 img

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

img

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-01 16:08:03  更:2022-05-01 16:08:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 17:49:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码