简略
推荐:《VFS、超级块、inode、dentry、file》https://www.itdaan.com/blog/2014/08/31/f99ae6e6e574449076a385cd07f0957b.html
https://www.itdaan.com/blog/2014/08/31/b60d5d86d0e61607dc9f1becfe0f5101.html
Linux虚拟文件系统四大对象:
1)超级块(super block)
2)索引节点(inode)
3)目录项(dentry)
4)文件对象(file)
索引节点inode:文件是由 inode 以及 inode指向的数据块构成,Inode记录了文件的管理信息,数据块记录文件的具体内容。
目录也是由 inode 以及inode指向的数据块构成 ,但目录的数据块 记录的是该目录下的 子目录/文件的 inode 以及 子目录名/文件名 等信息。
目录项dentry:目录项是描述文件的逻辑属性(dentry中包含了文件名,文件的inode号等信息。),只存在于内存中,更确切的说是存在于内存的目录项缓存,为了提高查找性能而设计。注意不管是文件夹还是最终的文件,都是属于目录项,所有的目录项在一起构成一颗庞大的目录树。例如:open一个文件/home/xxx/yyy.txt,那么/、home、xxx、yyy.txt都是一个目录项,VFS在查找的时候,根据一层一层的目录项找到对应的每个目录项的inode,那么沿着目录项进行操作就可以找到最终的文件。
dentry结构是一种含有指向父节点和子节点指针的双向结构,多个这样的双向结构构成一个内存里面的树状结构,也就是文件系统的目录结构在内存中的缓存了。有了这个缓存,我们在访问文件系统时,通常都非常快捷。
注意:目录也是一种文件(所以也存在对应的inode)。打开目录,实际上就是打开目录文件。
举个文件的例子:/home/user/Desktop/bilibili.txt ,假设 bilibili.txt和 / 在同一个文件系统,那么,只需要读 / inode读到 home并找到找到home 的inode 并读取,这样步步跳转,最后会读取bilibili.txt这文件对应的 inode ,好了,你打开 bilibili.txt这个文件并读取内容 时,VFS会调用ext3的read()(在5中的安装 ,函数已经向VFS注册过)去 读此inode对应的数据块。
文件对象:注意文件对象描述的是进程已经打开的文件。因为一个文件可以被多个进程打开,所以一个文件可以存在多个文件对象。但是由于文件是唯一的,那么inode就是唯一的,目录项也是定的!
进程其实是通过文件描述符来操作文件的,注意每个文件都有一个32位的数字来表示下一个读写的字节位置,这个数字叫做文件位置。一般情况下打开文件后,打开位置都是从0开始,除非一些特殊情况。Linux用file结构体来保存打开的文件的位置,所以file称为打开的文件描述。这个需要好好理解一下!file结构形成一个双链表,称为系统打开文件表。
Superblock, Inode, Dentry 和 File 都属于元数据(Metadata),
Linux系统从ext2开始,是将文件属性和文件内容分开存储,inode 用于存储文件的各属性,block 用来存储文件的内容。inode指向block(至少一个)。
Super block即为超级块,它是硬盘分区开头,超级块中的数据是卷资源表,有关文件卷的大部分信息都保存在这里。 例如:硬盘分区中每个block的大小、硬盘分区上一共有多少个block group、以及每个block group中有多少个inode。它定义了文件系统的类似、大小、状态,和其他元数据结构的信息(元数据的元数据)。
vfs四大对象:超级块、inode、dentry、file之间关系 - https://zhuanlan.zhihu.com/p/354100369
详细
读linux下文件--->陷入kernel-->VFS系统-->文件系统(例如ext3)-->访问物理硬件;
Superblock, Inode, Dentry 和 File 都属于元数据(Metadata),
1、Inode 和 Block
(1)背景
由于Linux系统从ext2开始,是将文件属性和文件内容分开存储的,分别由inode和block来负责。
(2)inode 用于存储文件的各属性
用于存储文件的各属性,包括:
- 所有者信息:文件的owner,group;
- 权限信息:read、write和excite;
-时间信息:建立或改变时间(ctime)、最后读取时间(atime)、最后修改时间(mtime);
- 标志信息:一些flags;
- 内容信息:type,size,以及相应的block的位置信息。
注意:不记录文件名或目录名,文件名或目录名记录在文件所在目录对应的block里。
(3)block 用来存储文件的内容。
(4)创建目录或文件
当创建一个目录时,文件系统会为该目录分配一个inode和至少一个block。该inode记录该目录的属性,并指向那块block。该block记录该目录下相关联的文件或目录的关联性和名字。
当创建一个文件时,文件系统会为该文件分配至少一个inode和与该文件大小相对应的数量的block。该inode记录该文件的属性,并指向block。
如果一个目录中的文件数太多,以至于1个block容纳不下这么多文件时,Linux的文件系统会为该目录分配更多的block。
(各block之间形成链表?)
(5)读取目录或文件
当我们告知操作系统一个文件的路径后,操作系统是如何找到这个文件的呢?首先操作系统会调用文件系统的相应接口,接下来:
递归说法:当读取一个文件或目录X时,提供给文件系统的是一个路径P。文件系统会先读取X所在的目录D的inode_d(注意这里,其实是这一操作的递归过程),通过inode_d获得其对应的block_d,在block_d中通过已知的X的名称,查询到X的inode_x。
迭代说法;如果读取的是/x1/x2/x3/x4/x5这个文件,则先读取根目录的inode_root,然后找到inode_root对应的block_root,在block_root中根据x1这个名字找到x1对应的inode_x1,然后找到inode_x1对应的block_x1,在block_x1中根据x2这个名字找到inode_x2,再找到block_x2,然后根据x3找到inode_x3,再block_x3,根据x4找到inode_x4,再block_x4,再根据x5找到inode_x5,再block_x5,就读取到我们要的x5的内容了。
Linux文件系统基础之inode和dentry
VFS:Linux系统中存在很多的文件系统ext2,ext3,ext4,sysfs,rootfs,proc..,为了能透明使用它们就需要VFS作为中间一层,用户直接和VFS打交道,例如read,write。
VFS在Linux系统中的结构为:
inode是内核文件对象的元数据。inode中不包括文件的数据和文件名字信息。inode仅仅只是保存了文件对象的属性信息,包括:权限、属组、数据块的位置、时间戳等信息。但是并没有包含文件名,文件在文件系统的目录树中所处的位置信息。
那么内核又是怎么管理文件系统的目录树呢?
dentry在内核中起到了连接不同的文件对象inode的作用,进而起到了维护文件系统目录树的作用。dentry是一个纯粹的内存结构,由文件系统在提供文件访问的过程中在内存中直接建立。dentry中包含了文件名,文件的inode号等信息。
在读取一个文件时,总是从根目录开始读取,每一个目录或者文件,在VFS中,都是一个文件对象,每一个文件对象都有唯一的一个inode与之对应。根目录的inode号为0,在superblock里,可以很快根据inode号索引到具体的inode,因此读取到的第一个inode就是根目录的。读取到了该目录后,内核对象会为该文件对象建立一个dentry,并将其缓存起来,方便下一次读取时直接从内存中取。而目录本身也是一个文件,目录文件的内容即是该目录下的文件的名字与inode号,目录文件的内容就像一张表,记录的文件名与其inode no.之间的映射关系。根据路径即可找到当前需要读取的下一级文件的名字和inode,同时继续为该文件建立dentry,dentry结构是一种含有指向父节点和子节点指针的双向结构,多个这样的双向结构构成一个内存里面的树状结构,也就是文件系统的目录结构在内存中的缓存了。有了这个缓存,我们在访问文件系统时,通常都非常快捷。
有了inode和dentry,也就非常容易理解文件的连接了。我们知道软连接,是一个特殊的文件,该文件通过内容指向目标文件。因此软连接有自己的inode,有自己的内容。其内容记录的是目标文件的inode号和自身的名字。软连接是一种特殊的文件。而硬链接则不一样,硬链接是文件的别名,硬链接不是一个完整的文件对象,硬链接只是将自己的名字写在上级目录的内容(文件名与inode no.的映射表)中。而其inode号即是目标文件的inode。这样硬连接与目标文件一起共用一个inode,使用引用计数来管理硬连接。
dentry
dentry是一个内存实体,其中的d_inode成员指向对应的inode
struct dentry {
atomic_t d_count;
struct inode * d_inode; //指向一个inode结构。这个inode和dentry共同描述了一个普通文件或者目录文件
struct dentry * d_parent; //父目录的目录项对象
struct list_head d_hash; //链接到dentry cache的hash链表。
struct list_head d_lru; //未使用链表的指针
struct list_head d_child;//dentry自身的链表头,需要链接到父dentry的d_subdirs成员
struct list_head d_subdirs;//项(子项可能是目录,也可能是文件)的链表头,所有的子项都要链接到这个链表
int d_mounted;// 指示dentry是否是一个挂载点。如果是挂载点,该成员不为零。
struct qstr d_name;// 成员保存的是文件或者目录的名字。打开一个文件的时候,根据这个成员和用户输入的名字比较来搜寻目标文件
...
};
当移动文件的时候,需要把一个dentry结构从旧的父dentry的链表上脱离,然后链接到新的父dentry的d_subdirs成员。这样dentry结构之间就构成了一颗目录树
每个dentry都有一个指向其父目录的指针(d_parent),一个子dentry的哈希列表(d_child)。其中,子dentry基本上就是目录中的文件
dentry状态:
1.d_count=0,未使用(unused)状态,d_inode指向相关的的索引节点。该目录项仍然包含有效的信息,只是当前没有人引用他。这种dentry对象在回收内存时可能会被释放。
2.d_count>0,正在使用(inuse)状态,d_inode指向相关的inode对象。这种dentry对象不能被释放。
3.d_count<0,y负(negative)状态,inode对象不复存在可能被删除,dentry对象的d_inode指针为NULL。但这种dentry对象仍然保存在dcache中,以便后续对同一文件名的查找能够快速完成。这种dentry对象在回收内存时将首先被释放。
dentry与dentry_cache
dentry_cache(dcache,目录项高速缓存)由两个数据结构组成:
1、哈希链表dentry_hashtable:dcache中的所有dentry对象都通过d_hash指针域链到相应的dentry哈希链表中。
2、未使用的dentry对象链表dentry_unused:dcache中所有处于unused状态和negative状态的dentry对象都通过其d_lru指针域链入dentry_unused链表中。该链表也称为LRU链表。
dcache中的dentry对象控制着icache中的inode对象的生命期转换。无论何时,只要一个目录项对象存在于dcache中(非 negative状态),则相应的inode就将总是存在,因为 inode的引用计数i_count总是大于0。当dcache中的一个dentry被释放时,针对相应inode对象的iput()方法就会被调用
dentry 和inode_尔容又夏的博客-CSDN博客_dentry和inode
Linux系统是怎样将文件路径转换到具体物理存储位置的?
Linux系统是怎样将文件路径转换到具体物理存储位置的?比如"/home/user/Desktop/bilibili.txt"
读linux下文件--->陷入kernel-->VFS系统-->文件系统(例如ext3)-->访问物理硬件;
作者:唐浩然
链接:https://www.zhihu.com/question/52899783/answer/132998267
这个所谓的“转换”是由VFS来完成的。下面拿 ext3来举例粗略讲一下这个过程,且我们暂时不考虑 dentry,inode的缓存 :
1:你看到的 /home/user/Desktop/bilibili.txt 是 已经安装/挂载 到操作系统的文件系统 中的 文件。
2:以普通方式使用磁盘或分区在使用前需要以具体的文件系统来格式化(mkfs.ext3),格式化是在磁盘上 建立 inode(每个inode有自己的 inode_id,在相同的文件系统,此id号唯一) 和数据块,以及inode和数据块的 管理 数据,比如Inode位图,以及超级块。
3:格式后化后的磁盘或分区需要安装(linux下叫挂载)到操作系统之后才可以使用。安装 时VSF会读取 磁盘中的管理 数据,比如 超级块数据,得到磁盘或分区上 inode和数据块的使用情况。
4:ext3的文件系统,
A:文件是由 inode 以及 inode指向的数据块构成,Inode记录了文件的管理信息,数据块记录文件的具体内容。
B:目录也是由 inode 以及inode指向的数据块构成 ,但目录的数据块 记录的是该目录下的 子目录/文件的 inode 以及 子目录名/文件名 等信息。
5:磁盘上的 根文件系统 是在 安装 操作系统时 已经格式化好的(建立/节点inode , 如4中所说,/ 的inode指向的数据块记录了 bin ,etc var ,usr 等目录的Inode_id及这些目录名字),并且在系统启动的时候,磁盘中上 根文件系统 被 安装 到 VFS的 / 节点,(安装时会向VFS注册用来操作ext3文件系统时所使用的函数),相当于 VFS根据磁盘上 / 的 inode信息在内存中 建立一个 inode结构,但VFS的inode比磁盘上 根节点 Inode的信息要丰富,但inode_id是一样的。 当你执行ls时,相当于从磁盘 读取 / 的inode 指向的数据块内容(其实系统在启动时已经读取并缓存至内存了,目录项名对应 dentry结构,这只是VFS在内存中建立的结构,磁盘上没有对应的结构) ,你就可以看到 bin ,etc,var, usr 这些内容 。
6:我们看看你举这个文件的例子:/home/user/Desktop/bilibili.txt ,假设 bilibili.txt和 / 在同一个文件系统,那么,只需要读 / inode读到 home并找到找到home 的inode 并读取,这样步步跳转,最后会读取bilibili.txt这文件对应的 inode ,好了,你打开 bilibili.txt这个文件并读取内容 时,VFS会调用ext3的read()(在5中的安装 ,函数已经向VFS注册过)去 读此inode对应的数据块。
7:如果 你的 例子中,bilibili.txt和 / 不在同一个文件系统,假设,你是在 /home 下面挂载了另一个磁盘分区,此时,VFS 在内存中 home 目录的inode 已经不再指向 原来数据,而指向了你 新挂载的 磁盘分区 的 根目录 ,所以此时你查看 /home 下内容 时,看到了 你另一块磁盘中的usr 目录 ,下面的步骤和6中 一样的,经过层层跳转,最后读到 bilibil.txt的inode及其指向的数据块。
Linux里面最大的套路是“一切都是文件”?
关注者
inode源头,file活水
我们把文件想象成一个object,那么inode描述的是本源,和最终的object一一对应;dentry是inode的一个路径马甲,比如我们可以通过"ln"命令为同一个inode创建很多的硬链接马甲;而file则是活水,进程对object的一次“open”,获得一个file,导致用户态得到一个"fd"的句柄来操作这个object。
经典的inode、dentry、file谁都不缺席的模型是这样的:
上图中,我们有一个inode,这个inode有2个dentry,进程A、B open的是第一个dentry;而进程C、D open的是第二个dentry。变了的是file和fd,不变的是inode,中间的dentry马甲没那么重要。
但是在inode、dentry、file这个经典铁三角中,从来都是可以有一个缺席者的,那就是dentry,因为,有时候用户态想获得长城内外行走的便利,但是却不想这个inode在文件系统里面留下一个路径的痕迹。简单来说,我希望有个fd,但是这个fd,你在从"/"往下面搜索的任何一条路径下,你都找不到它,它根本在根文件系统以下不存在路径,它是无名氏,它没有马甲,它是个传说。
https://i.stack.imgur.com/daHCZ.gif
https://www.apriorit.com/images/articles/hidedriverlinux1.png