文档章节

Linux VFS 虚拟文件系统数据结构

满小茂
 满小茂
发布于 2015/12/26 20:48
字数 2904
阅读 512
收藏 2
点赞 0
评论 0

 

虚拟文件系统‍

虚拟文件系统
         虚拟文件系统(VFS)是linux内核和具体I/O设备之间的封装的一层共通访问接口,通过这层接口,linux内核可以以同一的方式访问各种I/O设备。

 

虚拟文件系统(VFS)是linux内核和存储设备之间的抽象层,主要有以下好处:

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

--简化了新文件系统加入内核的过程:新文件系统只要实现VFS的各个接口即可,不需要修改内核部分

虚拟文件中的4个对象

 

       (1)  超级块 (2)索引节点 (3)目录项 (4)文件对象

超级块

它一般存储在磁盘的特定扇区中,但是对于那些基于内存的文件系统(比如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调用该函数中断安装操作 */
};

 索引节点

索引节点是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 *);
};

通过 inode (inode->i_ino)号和相关超级块 inode->i_sb可获取逻辑块组号:方式如下

block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);

 

目录项

  和超级块和索引节点不同,目录项并不是实际存在于磁盘上的。

在使用的时候在内存中创建目录项对象,其实通过索引节点已经可以定位到指定的文件,

但是索引节点对象的属性非常多,在查找,比较文件时,直接用索引节点效率不高,所以引入了目录项的概念。

每个目录项对象都有三种状态:被使用,未使用和负状态

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

- 未使用:对应一个有效的索引节点,但是VFS当前并没有使用这个目录项

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

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

目录项定义在<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);
};

 

文件对象

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

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

其实只有目录项对象才表示一个已打开的实际文件,虽然一个文件对应的文件对象不是唯一的,但其对应的索引节点和目录项对象却是唯一的。

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

//路径及结构体,包含目录项
struct path {
	struct vfsmount *mnt; //文件系统挂载使用
	struct dentry *dentry;
};

/* 
 * 文件对象结构中定义的字段非常多,
 * 这里只介绍一些重要的属性
 */
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);
};

 5 四个对象之间关系图

 

VFS 通用文件模型中包含以下四种元数据结构:

  1. 超级块对象(superblock object),用于存放已经注册的文件系统的信息。比如ext2,ext3等这些基础的磁盘文件系统,还有用于读写socket的socket文件系统,以及当前的用于读写cgroups配置信息的 cgroups 文件系统等。

  2. 索引节点对象(inode object),用于存放具体文件的信息。对于一般的磁盘文件系统而言,inode 节点中一般会存放文件在硬盘中的存储块等信息;对于socket文件系统,inode会存放socket的相关属性,而对于cgroups这样的特殊文件系统,inode会存放与 cgroup 节点相关的属性信息。这里面比较重要的一个部分是一个叫做 inode_operations 的结构体,这个结构体定义了在具体文件系统中创建文件,删除文件等的具体实现。

  3. 文件对象(file object),一个文件对象表示进程内打开的一个文件,文件对象是存放在进程的文件描述符表里面的。同样这个文件中比较重要的部分是一个叫 file_operations 的结构体,这个结构体描述了具体的文件系统的读写实现。当进程在某一个文件描述符上调用读写操作时,实际调用的是 file_operations 中定义的方法。 对于普通的磁盘文件系统,file_operations 中定义的就是普通的块设备读写操作;对于socket文件系统,file_operations 中定义的就是 socket 对应的 send/recv 等操作;而对于cgroups这样的特殊文件系统,file_operations 中定义的就是操作 cgroup 结构体等具体的实现。

  4. 目录项对象(dentry object),在每个文件系统中,内核在查找某一个路径中的文件时,会为内核路径上的每一个分量都生成一个目录项对象,通过目录项对象能够找到对应的 inode 对象,目录项对象一般会被缓存,从而提高内核查找速度。

 

© 著作权归作者所有

共有 人打赏支持
满小茂
粉丝 65
博文 117
码字总数 126003
作品 0
成都
程序员
Linux小知识-5:内核结构

Linux内核主要由五个部分组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。 稍微加以理解:CPU资源——内存资源——外存资源——外部资源——通信。 进程调度(SCHED):控制进...

lp_king
2013/09/28
0
0
三种方式在CentOS 7搭建KVM虚拟化平台

KVM 全称是基于内核的虚拟机(Kernel-based Virtual Machine),它是一个 Linux的一个内核模块,该内核模块使得 Linux变成了一个Hypervisor:它由 Quramnet开发,该公司于 2008年被 Red Hat ...

杨书凡
06/16
0
0
Linux文件系统的设计

总论: linux的文件系统设计非常优秀,总的来讲有两大部分,第一部分就是树形的组织结构,第二部分就是vfs,树形的组织结构组织了文件系统的表象,用户很方便的使用,而vfs是文件系统的实现机...

晨曦之光
2012/04/10
260
0
Linux文件系统—源代码导读

众所周知,文件系统是Unix系统最基本的资源。最初的Unix系统一般都只支持一种单一类型的文件系统,在这种情况下,文件系统的结构深入到整个系统内核中。而现在的系统大多都在系统内核和文件系...

nothingfinal
2012/09/21
0
0
【转】Linux 概念架构的理解

转:http://mp.weixin.qq.com/s?biz=MzA3NDcyMTQyNQ==&mid=400583492&idx=1&sn=3b18c463dcc45103b76a3419ceabe84c&scene=2&srcid=1213Z5CBO8W4jCmTSFI74uIb&from=timeline&isappinstalled=0#......

张玉涛
2015/12/19
0
0
Linux文件系统2---VFS的四个主要对象

https://www.cnblogs.com/smartjourneys/p/7260911.html 1.引言 本文所述关于文件管理的系列文章主要是对陈莉君老师所讲述的文件系统管理知识讲座的整理。 Linux可以支持不同的文件系统,它源...

chungle2011
05/11
0
0
电子取证工具软件--PyFlag

PyFlag 是一个使用 python 语言编写电子取证工具软件。PyFlag 工作流程如图 6。 图 PyFlag 工作流程 安装 PyFlag PyFlag 安装比较困难,特别是对于 Linux 初级用户。首先要建立一个完整的 AM...

匿名
2011/02/12
3.7K
0
转帖-linux文件系统

什么是文件系统? 首先回答最常见的问题,“什么是文件系统”。文件系统是对一个存储设备上的数据和元数据进行组织的机制。 查看方式:df -T 文件系统作为协议 另一种看待文件系统的方式是把...

erichd
2015/09/02
18
0
Linux内核源码学习 (0)- 源代码的结构

截止2012年11月4日,Linux的最新稳定版本为3.6.5,可以从Linux官方网站(http://www.kernel.org/)下载最新的版本,我所研究的内核版本是2.6.34.13,这一系列笔记都是对这个版本的内核进行分...

notishell
2013/01/02
0
0
Linux 虚拟文件系统和进程的关系

VFS中还有2个专门针对文件系统的2个对象, - struct filesystemtype: 用来描述文件系统的类型(比如ext3,ntfs等等) - struct vfsmount : 描述一个安装文件系统的实例 filesystemtype 结构体...

满小茂
2015/12/26
124
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

数据库事务的四大特性以及事务的隔离级别

本篇讲述数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别。 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是...

Java大蜗牛
10分钟前
0
0
Spring Boot 整合 MyBatis/通用Mapper/PageHelper分页插件

整合MyBatis 整合通用Mapper 1. POM依赖配置 <properties><mapper.starter.version>2.0.3-beta1</mapper.starter.version></properties><!-- 通用Mapper --><dependency><groupId>t......

OSC_fly
18分钟前
0
0
CentOS7 双网卡绑定

环境 操作系统 CentOS7.5,禁用 NetworkManager 服务 网卡 eth0 网卡 eth1 绑定网卡 bond0 网卡 eth0 配置 修改 /etc/sysconfig/network-scripts/ifcfg-eth0 TYPE=EthernetBOOTPROTO=noneD......

Colben
20分钟前
0
0
zk实战--rpc框架集群化

在看此篇内容时需要浏览下面内容 netty实战--手写rpc框架 前文功能简介以及功能扩充 利用netty来实现一个点对点的rpc调用。客户端和服务端都是靠手写地址进行socket同学的,无法1对多,也无法...

xpbob
36分钟前
11
0
springboot 发送邮件

获取授权码 添加配置 # 账号和密码spring.mail.username=aaa@qq.comspring.mail.password=bbb# 服务器地址spring.mail.host=smtp.qq.comspring.mail.properties.mail.smtp.ssl.en...

阿豪boy
37分钟前
0
0
如何使用GNU Ring?

文章名:如何使用GNU Ring? 作者:冰焰火灵X 1079092922@qq.com 文章许可:CC BY-SA 4.0 ##1. 安装 下载GNU Ring 点击左边选择你的系统版本(这里以 GNU/Linux 为例,我使用的是Mint 18.3)...

ICE冰焰火灵X
40分钟前
4
0
深入理解springMVC

什么是spring MVC Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而...

Java填坑之路
45分钟前
1
0
《射雕英雄传》书摘

1. 我虽是个飘泊江湖的贫家女子,可不是低三下四、不知自爱之人。你如真心爱我,须当敬我重我。我此生决无别念,就是钢刀架颈,也决意跟定了你。将来……将来如有洞房花烛之日,自然……自能...

k91191
56分钟前
1
0
解决:modal中datePicker 选中时,会触发modal的hidden.bs.modal事件

最近项目中发现了一个bug,具体表现为选中模态框上datepicker组件上的日期时,会触发模态框的关闭事件,导致数据编辑无法正常进行。网上搜索了下,解决方法如下: $('.datepicker').on('hid...

Funcy1122
59分钟前
0
0
Redis分布式锁的正确实现方式

前言 分布式锁一般有三种实现方式: 1.数据库乐观锁 2.基于Redis的分布式锁; 3.基于Zookeeper的分布式锁。本篇博客将介绍第二种方式,基于Redis实现分布式锁。虽然网上已经有各种介绍Redis...

大海201506
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部