深度解析linux的文件系统

news2024/11/29 8:47:00

背景:

虚拟文件系统(有时也称作虚拟文件交换,更常见的是简称VFS)作为内核子系统,为用户空间程序提供了文件和文件系统相关的接口。系统中所有文件系统不但依赖VFS共存,而且也依靠VFS系统协同工作。通过虚拟文件系统,程序可以利用标准的Uinx系统调用对不同的文件系统,甚至不同介质上的文件系统进行读写操作。

实现:

VFS执行的动作:使用cp(1)命令从ext3文件系统格式的硬盘拷贝数据到ext2文件系统格式的可移动磁盘上。两种不同的文件系统,两种不同的介质,连接到同一个 VFS 上。

Unix的文件系统:

概述:

1.Unix使用了四种和文件系统相关的传统抽象概念:文件、目录项、索引节点和安装点( mount point)。

从本质上讲文件系统是特殊的数据分层存储结构,它包含文件、目录和相关的控制信息。文件系统的通用操作包含创建、删除和安装等。

2.在 Unix中,文件系统被安装在一个特定的安装点上该安装点在全局层次结构中被称作命名空间,所有的已安装文件系统都作为根文件系统树的枝叶出现在系统中

3.文件:文件其实可以做一个有序字节串,字节串中第一个字节是文件的头,最后一个字节是文件的尾。每一个文件为了便于系统和用户识别,都被分配了一个便于理解的名字。典型的文件操作有读、写、创建和删除等。简单的面向字节流抽象的Unix文件则以简单性和相当的灵活性为代价

4.目录:文件通过目录组织起来。文件目录好比一个文件夹,用来容纳相关文件。因为目录也可以包含其他目录,即子目录,所以目录可以层层嵌套,形成文件路径。路径中的每一部分都被称作目录条目。“/home/wolfman/butter”是文件路径的一个例子——根目录/,目录home,wolfman和文件 butter都是目录条目,它们统称为目录项。在Unix中,目录属于普通文件,它列出包含在其中的所有文件。由于VFS把目录当作文件对待,所以可以对目录执行和文件相同的操作。

5.文件相关信息:Unix系统将文件的相关信息和文件本身这两个概念加以区分,例如访问控制权限、大小、拥有者、创建时间等信息。文件相关信息,有时被称作文件的元数据(也就是说,文件的相关数据),被存储在一个单独的数据结构中,该结构被称为索引节点( inode),它其实是index node的缩写,不过近来术语“inode”使用得更为普遍一些。

所有这些信息都和文件系统的控制信息密切相关,文件系统的控制信息存储在超级块中,超级块是一种包含文件系统信息的数据结构。有时,把这些收集起来的信息称为文件系统数据元,它集单独文件信息和文件系统的信息于一身。

VFS介绍:

VFS使得用户可以直接使用open()、read)和write()这样的系统调用而无须考虑具体文件系统和实际物理介质。现在听起来这并没什么新奇的(我们早就认为这是理所当然的),但是,使得这些通用的系统调用可以跨越各种文件系统和不同介质执行,绝非是微不足道的成绩。更了不起的是,系统调用可以在这些不同的文件系统和介质之间执行——我们可以使用标准的系统调用从一个文件系统拷贝或移动数据到另一个文件系统。

老式的操作系统(比如DOS〉是无力完成上述工作的,任何对非本地文件系统的访问都必须依靠特殊工具才能完成。正是由于现代操作系统引入抽象层,比如Linux,通过虚拟接口访问文件系统,才使得这种协作性和泛型存取成为可能。

文件系统抽象层:

VFS抽象层之所以能衔接各种各样的文件系统,是因为它定义了所有文件系统都支持的、基本的、概念上的接口和数据结构。同时实际文件系统也将自身的诸如“如何打开文件”,“目录是什么”等概念在形式上与VFS的定义保持一致。因为实际文件系统的代码在统一的接口和数据结构下隐藏了具体的实现细节,所以在VFS层和内核的其他部分看来,所有文件系统都是相同的,它们都支持像文件和目录这样的概念,同时也支持像创建文件和删除文件这样的操作。

内核通过抽象层能够方便、简单地支持各种类型的文件系统。实际文件系统通过编程提供VFS所期望的抽象接口和数据结构,这样,内核就可以毫不费力地和任何文件系统协同工作,并这样提供给用户空间的接口,也可以和任何文件系统无绛地连接在一起,完成实际工作。

一个简单的用户空间操作:

ret=write(fd,buf,len)

该系统调用将buf指针指向的长度为len字节的数据写入文件描述符fd对应的文件的当前位置。

这个系统调用首先被一个通用系统调用sys_write()处理,sys_write()函数要找到fd所在的文件系统实际给出的是哪个写操作,然后再执行该操作。实际文件系统的写方法是文件系统实现的一部分,数据最终通过该操作写入介质(或执行这个文件系统想要完成的写动作)。

下图描述了从用户空间的write()调用到数据被写入磁盘介质的整个流程。

一方面,系统调用是通用VFS接口,提供给用户空间的前端﹔

另一方面,系统调用是具体文件系统的后端,处理实现细节。

VFS的对象

VFS其实采用的是面向对象的设计思路,使用一组数据结构来代表通用文件对象。这些数据结构类似于对象。因为内核纯粹使用C代码实现,没有直接利用面向对象的语言,所以内核中的数据结构都使用C语言的结构体实现,而这些结构体包含数据的同时也包含操作这些数据的函数指针,其中的操作函数由具体文件系统实现。

VFS中有四个主要的对象类型,它们分别是:

超级块对象:它代表一个具体的已安装文件系统。

索引节点对象:它代表一个具体文件。

目录项对象,它代表一个目录项,是路径的一个组成部分。

文件对象,它代表由进程打开的文件。

每个主要对象中都包含一个操作对象,这些操作对象描述了内核针对主要对象可以使用的方法:

super_operations对象,其中包括内核针对特定文件系统所能调用的方法,比如 write_inode()和sync_fs()等方法。

inode_operations对象,其中包括内核针对特定文件所能调用的方法,比如create()和link)等方法。

dentry_operations对象,其中包括内核针对特定目录所能调用的方法,比如d_compare()和d_delete(等方法。

file_operations对象,其中包括进程针对已打开文件所能调用的方法,比如read()和write(等方法。

超级块对象

各种文件系统都必须实现超级块对象,该对象用于存储特定文件系统的信息,通常对应于存放在磁盘特定扇区中的文件系统超级块或文件系统控制块(所以称为超级块对象)。对于并非基于磁盘的文件系统(如基于内存的文件系统,比如sysfs),它们会在使用现场创建超级块并将其保存到内存中

引用于:/usr/src/kernels/3.10.0-1160.el7.x86_64/include/linux/fs.h

struct super_block {
        struct list_head        s_list;         /* Keep this first */
        dev_t                   s_dev;          /* search index; _not_ kdev_t */
        unsigned char           s_blocksize_bits;
        unsigned long           s_blocksize;
        loff_t                  s_maxbytes;     /* Max file size */
        struct file_system_type *s_type;
        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;
        unsigned long           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
        const struct xattr_handler **s_xattr;

        struct list_head        s_inodes;       /* all inodes */
       struct hlist_bl_head    s_anon;         /* anonymous dentries for (nfs) exporting */
#ifdef __GENKSYMS__
#ifdef CONFIG_SMP
        struct list_head __percpu *s_files;
#else
        struct list_head        s_files;
#endif
#else
#ifdef CONFIG_SMP
        struct list_head __percpu *s_files_deprecated;
#else
        struct list_head        s_files_deprecated;
#endif
#endif
        struct list_head        s_mounts;       /* list of mounts; _not_ for fs use */
        /* s_dentry_lru, s_nr_dentry_unused protected by dcache.c lru locks */
        struct list_head        s_dentry_lru;   /* unused dentry lru */
        RH_KABI_REPLACE_UNSAFE(
                        int     s_nr_dentry_unused,
                        long    s_nr_dentry_unused)     /* # of dentry on lru */
                                                              1401,2-9      41%
...
}

创建、管理和撤销超级块对象的代码位于文件 fs/super.c中。超级块对象通过alloc_super()函数创建并初始化。在文件系统安装时,文件系统会调用该函数以便从磁盘读取文件系统超级块,并且将其信息填充到内存中的超级块对象中。

超级块的操作函数

超级块对象中最重要的一个域是s_op,它指向超级块的操作函数表。超级块操作函数表由iper operations结构休表示,定义在文件<linux/fs.h>中,其形式如下;

struct super_operations {
        struct inode *(*alloc_inode)(struct super_block *sb);
        void (*destroy_inode)(struct inode *);

        void (*dirty_inode) (struct inode *, int flags);
        int (*write_inode) (struct inode *, struct writeback_control *wbc);
        int (*drop_inode) (struct inode *);
        void (*evict_inode) (struct inode *);
        void (*put_super) (struct super_block *);
        int (*sync_fs)(struct super_block *sb, int wait);
        int (*freeze_fs) (struct super_block *);
        int (*unfreeze_fs) (struct super_block *);
        int (*statfs) (struct dentry *, struct kstatfs *);
        int (*remount_fs) (struct super_block *, int *, char *);
        void (*umount_begin) (struct super_block *);

        int (*show_options)(struct seq_file *, struct dentry *);
        int (*show_devname)(struct seq_file *, struct dentry *);
        int (*show_path)(struct seq_file *, struct dentry *);
        int (*show_stats)(struct seq_file *, struct dentry *);
#ifdef CONFIG_QUOTA
        ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
        ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
#endif
        int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
        int (*nr_cached_objects)(struct super_block *);
        void (*free_cached_objects)(struct super_block *, int);
        RH_KABI_EXTEND(struct list_head *(*inode_to_wblist)(struct inode *))
        RH_KABI_EXTEND(struct inode *(*wblist_to_inode)(struct list_head *))
};

该结构体中的每一项都是一个指向超级块操作函数的指针,超级块操作函数执行文件系统和索引节点的低层操作。

当文件系统需要对其超级块执行操作的时候,首先要在超级块的对象中寻找需要的操作方法。

sb->s_op->write_super(sb);
/*
在这个调用中,sb是指向文件系统超级块的指针,沿着该指针进入超级块操作函数表s_op,并从表中取得希望得到的write_super()函数,该函数执行写入超级块的实际操作。注意,尽管write_super)方法来自超级块,但是在调用时,还是要把超级块作为参数传递给它,这是因为C缺少对面向对象的支持
*/

由于在C语言中无法直接得到操作函数的父对象,所以必须将父对象以参数形式传给操作函数。

下面给出super_operation中,超级块操作函数的用法

struct inode *alloc_inode (struct super_block *sb)

在给定的超级块下创建和初始化一个新的索引节点对象。

void destroy_inode (struct inode *inode)

释放给定的索引节点

void dirty_inode(struct inode *inode)

VFS在索引节点脏(被修改)时会调用此函数。日志文件系统(如ext3和ext4)执行该函数进行日志更新。

 void write _inode (struct inode *inode ,int wait)

用于将给定的索引节点写入磁盘。wait参数指明写操作是否需要同步

void drop_inode(struct inode *inode)

在最后一个指向索引节点的引用被释放后,VFS会调用该函数。VFS只需要简单地删除这个索引节点后,普通Unix文件系统就不会定义这个函数了。

void de1ete_inode(struct inode *inode)

用于从磁盘上删除给定的索引节点。

void put_super(struct super_block *sb)

在卸载文件系统时由VFS 调用,用来释放超级块。调用者必须一直持有s_lock锁

oid write_super(struct super_block *sb)

用给定的超级块更新磁盘上的超级块。VFS通过该函数对内存中的超级块和磁盘中的超级块进行同步。调用者必须一直持有s_lock锁。

 int sync_fs (struct super_block *sb, int wait)

使文件系统的数据元与磁盘上的文件系统同步。wait参数指定操作是否同步。

索引节点对象

索引节点对象包含了内核在操作文件或目录时需要的全部信息。

对于Unix风格的文件系统来说

这些信息可以从磁盘索引节点直接读入。

如果一个文件系统没有索引节点,那么,不管这些相关信息在磁盘上是怎么存放的,文件系统都必须从中提取这些信息。

没有索引节点的文件系统通常将文件的描述信息作为文件的一部分来存放。

这些文件系统与Unix风格的文件系统不同,没有将数据与控制信息分开存放。有些现代文件系统使用数据库来存储文件的数据。不管哪种情况、采用哪种方式,索引节点对象必须在内存中创建,以便于文件系统使用。

引自:/usr/src/kernels/3.10.0-1160.el7.x86_64/include/linux/fs.h

struct inode {
        umode_t                 i_mode;
        unsigned short          i_opflags;
        kuid_t                  i_uid;
        kgid_t                  i_gid;
        unsigned int            i_flags;

#ifdef CONFIG_FS_POSIX_ACL
        struct posix_acl        *i_acl;
        struct posix_acl        *i_default_acl;
#endif

        const struct inode_operations   *i_op;
        struct super_block      *i_sb;
        struct address_space    *i_mapping;

#ifdef CONFIG_SECURITY
        void                    *i_security;
#endif
...
}

一个索引节点代表文件系统中(但是索引节点仅当文件被访问时,才在内存中创建〉的一个文件,它也可以是设备或管道这样的特殊文件。因此索引节点结构体中有一些和特殊文件相关的项,比如i_pipe项就指向一个代表有名管道的数据结构,i_bdev指向块设备结构体,i_cdev指向字符设备结构体。这三个指针被存放在一个公用体中,因为一个给定的索引节点每次只能表示三者之一(或三者均不)。

索引节点操作

struct inode_operations {
        struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
        void * (*follow_link) (struct dentry *, struct nameidata *);
        int (*permission) (struct inode *, int);
        struct posix_acl * (*get_acl)(struct inode *, int);

        int (*readlink) (struct dentry *, char __user *,int);
        void (*put_link) (struct dentry *, struct nameidata *, void *);

        int (*create) (struct inode *,struct dentry *, umode_t, bool);
        int (*link) (struct dentry *,struct inode *,struct dentry *);
        int (*unlink) (struct inode *,struct dentry *);
        int (*symlink) (struct inode *,struct dentry *,const char *);
        int (*mkdir) (struct inode *,struct dentry *,umode_t);
        int (*rmdir) (struct inode *,struct dentry *);
        int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
        int (*rename) (struct inode *, struct dentry *,
                        struct inode *, struct dentry *);
        int (*setattr) (struct dentry *, struct iattr *);
        int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
        int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
        ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
...
}

下面这些接口由各种函数组成,在给定的节点上,可能由VFS执行这些函数,也可能由具体的文件系统执行:

 int create(struct inode *dir,struct dentry *dentry,int mode)

VFS通过系统调用create()和open()来调用该函数,从而为dentry对象创建一个新的索引节点。在创建时使用mode指定的初始模式。

struct dentry * lookup(struct inode *dir,struct dentry *dentry)

该函数在特定目录中寻找索引节点,该索引节点要对应于denrty中给出的文件名。

 int link(struct dentry *old_dentry,struct inode *dir,struct dentry *dentry)

该函数被系统调用link()调用,用来创建硬连接。硬连接名称由dentry参数指定,连接对象是dir目录中old_dentry目录项所代表的文件。

 int unlink (struct inode *dir,struct dentry *dentry)

该函数被系统调用unlink()调用,从目录dir中删除由目录项dentry 指定的索引节点对象。

int symlink (struct inode *dir,struct dentry *dentry.const char symname)

该函数被系统调用symlik()调用,创建符号连接。该符号连接名称由symname指定,连接对象是dir目录中的dentry目录项。

 int mkdir(struct inode *dir,struct dentry *dentry.int mode)

该函数被系统调用mkdir()调用,创建一个新目录。创建时使用mode指定的初始模式

nt rmdir(struct inode *dir.struct dentry dentry)

调用rmdir()调用,删除dir目录中的dentry目录项代表的文件

...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/367434.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

动态规划(一):01背包问题和完全背包问题

动态规划 目录动态规划1.01背包问题1.1题目介绍1.2思路一介绍(二维数组)1.3思路二介绍(一维数组) 空间优化1.4思路三介绍(输入数据优化)2.完全背包问题2.1题目描述&#xff1a;2.2思路一(朴素算法)2.3思路二(将k优化处理掉)2.4思路三(优化j的初始条件)总结1.01背包问题 1.1题目…

求职-进度

2-23 投递 恒生校招 C https://campus.hundsun.com/personal/deliveryRecord 投递 合合信息 大数据开发工程师 https://intsig.zhiye.com/personal/deliveryRecord 投递 尚游网络 游戏服务器开发工程师 https://app.mokahr.com/campus_apply/shangyou/36582?recommendCodeDS…

项目管理中,哪些信息差是不应该存在的呢?

在项目管理中&#xff0c;如果存在信息差&#xff0c;那么就会存在了巨大的问题&#xff0c;从而导致项目的失败。 项目管理中哪些信息差是不应该存在的呢&#xff1f; 1、项目背景 项目经理接手项目&#xff0c;首先要了解清楚项目背景&#xff0c;避免在项目过程中对自己…

计算句子向量相似度:SentenceBert和SimCSE

SentenceBert Sentence-BERT: 如何通过对比学习得到更好的句子向量表示 - 哔哩哔哩 (bilibili.com) (229条消息) Sentence-BERT详解_数学家是我理想的博客-CSDN博客_sentence-bert 动机&#xff1a; 直接把2个句子串联起来输入Bert做分类&#xff08;即Cross-Encoder方式&…

Blazor入门100天 : 身份验证和授权 (6) - 使用 FreeSql orm 管理ids数据

目录 建立默认带身份验证 Blazor 程序角色/组件/特性/过程逻辑DB 改 Sqlite将自定义字段添加到用户表脚手架拉取IDS文件,本地化资源freesql 生成实体类,freesql 管理ids数据表初始化 Roles,freesql 外键 > 导航属性完善 freesql 和 bb 特性 本节源码 https://github.com/…

Maven基础-又简单又详细

如果文章对你有帮助欢迎【关注❤️❤️❤️点赞&#x1f44d;&#x1f44d;&#x1f44d;收藏⭐⭐⭐】一键三连&#xff01;一起努力&#xff01; 一、Maven简介 1、maven是什么 Maven的本质是一个项目管理工具&#xff0c;将项目开发和管理过程抽象成一个项目对象模型&#x…

JavaTCP通信程序

3 TCP通信程序 3.1 TCP通信原理 TCP通信协议是一种可靠的网络协议&#xff0c; 它在通信的两端名建立一个Socke对象&#xff0c; 从而在通信的两端形成网络虚拟链路一旦建立了 虚拟的网络链路&#xff0c;两端的程序就可以通过虚拟链路进行通信Java对基于TCP协议的的网络提供…

python3.11.2安装 + pycharm安装

下载 &#xff1a;https://www.python.org/ 2.双击下载的软件&#xff1a; 3.进入安装界面 下一步&#xff0c;点击 是 上一步点击后就看到如下&#xff1a; 安装成功了&#xff0c;接下来检测一下&#xff1a;cmd 安装pycharm PyCharm是一种Python IDE&#xff08;Integr…

office三件套与mathtype的安装和导入word

0. 前言 网上大部分说的不够具体&#xff0c;说的比较具体的就是下面这篇文章&#xff0c;但关键他路径还是错的。 MathType如何导入word 可能因为他是32位系统&#xff0c;所以office中某些路径和设置不一样&#xff0c;下文中一一指出。 mathtype导入word0. 前言1. 安装1.…

springboot整合springdata jpa全能书

一&#xff1a;spring data jpa介绍 spring data:其实spring data就是spring提供了一个操作数据的框架。而spirng data jpa只是spring data框架下的一个基于jpa标准操作数据的模块。 spring data jpa&#xff1a;基于jpa的标准对数据进行操作。简化操作持久层的代码。只需要编…

GEE学习笔记 五十二:Google Earth Studio初体验

Google Earth Studio出来一段时间了&#xff0c;自己也体验了一番。这里做一个简单的体验总结&#xff0c;为那些还没有体验过的小伙伴展示一下Google Earth Sutdio究竟长什么样子&#xff0c;能做什么&#xff1f; 注&#xff1a;这篇文章营养价值不大&#xff0c;纯粹是展示…

京东测开岗3+1面经+经验分享,拿到offer,月薪34k....

现在&#xff0c;招聘黄金时间已经来临&#xff0c;在网上看了很多大佬的面经&#xff0c;也加了很多交流群&#xff0c;受到了很多朋友的提点&#xff0c;今天终于轮到我来分享面经啦&#xff0c;之前面试了几家公司&#xff0c;最后拿到了京东测试岗的 offer&#xff0c;这里…

【机器学习、深度学习】损失函数

1.什么是损失函数 ​ 损失函数&#xff08;Loss Function&#xff09;又叫做误差函数&#xff0c;用来衡量算法拟合数据的好坏程度&#xff0c;评价模型的预测值与真实值的不一致程度&#xff0c;是一个非负实值函数&#xff0c;通常使用L(Y, f(x))​来表示&#xff0…

canvas初学1

前端数据可视化方案&#xff1a; 一、canvas绘制直线 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewpor…

CentOS 8利用Apache安装部署文件服务器

1&#xff1a;部署的目的是做一个类似下面开源镜像网站&#xff0c;把一些软件或者资料上传到服务器上面&#xff0c;减少用户在互联网上下载资料&#xff0c;提高效率&#xff0c;减少病毒。 2&#xff1a;使用下面的命令配置本机的IP地址主机名等信息。后期使用IP地址进行访问…

任意网络环境实现外网访问分销ERP

随着企业业务的不断扩展&#xff0c;经营网点遍布全国不同的区域&#xff0c;传统的管理手段存在诸多问题&#xff0c;无法实时监控各地分公司、办事处及营业网点的经营状况&#xff1b;订货、销售、库存等数据和信息反馈不及时&#xff0c;商品积压、缺货情况经常出现&#xf…

软件项目管理知识回顾---软件项目进度管理

软件项目进度管理 4.进度管理 4.1进度管理 1.概念&#xff1a;按时保质的完成任务 2.目的&#xff1a;按时完成任务&#xff0c;合理分配资源&#xff0c;发挥最佳的工作效率 3.活动&#xff1a;工作包分解出的进度活动 4.内容&#xff1a;项目进度计划的指定和项目进度计划的执…

一篇了解分布式id生成方案

系统唯一ID是我们在设计一个系统的时候常常会遇见的问题&#xff0c;也常常为这个问题而纠结。生成ID的方法有很多&#xff0c;适应不同的场景、需求以及性能要求。所以有些比较复杂的系统会有多个ID生成的策略。下面就介绍一些常见的ID生成策略。 1.数据库自增长序列或字段 …

BabylonJS之放烟花

BabylonJS烟花效果视频一&#xff1a; 技术调研 1. 方案一&#xff1a;ParticleSystem 用ParticleSystem来实现每一束的烟花效果&#xff0c;如果浏览器支持WebGL2功能&#xff0c;使用GPUParticleSystem性能会有极大的提升。 优点&#xff1a; 烟花效果易实现且效果好。 缺点…

什么是品牌营销?学会正确推广您的业务

什么是品牌营销&#xff1f; 品牌营销涉及长期战略规划&#xff0c;以推广整个品牌&#xff0c;而不是营销单个产品或服务。它分享了一个引人入胜的故事&#xff0c;以在潜在客户中产生品牌知名度并建立声誉。 面向消费者的品牌使用品牌智能软件来了解人们对其品牌的看法&#…