【Linux系统】Ext系列磁盘文件系统二:引入文件系统(续篇)




在这里插入图片描述



inodeblock 的映射


该博文中有详细解释:【Linux系统】inode 和 block 的映射原理


目录与文件名

这里有几个问题:

问题一: 我们访问文件,都是用的文件名,没用过 inode 号啊?

之前总是说可以通过一个文件的 inode 号就可以获取该磁盘文件的相关信息

但是,我们凭什么可以获得 inode

我们用户操作好像不是通过 inode 操作的,而是通过文件名操作的!

问题二:文件名存储在哪里呢?

之前说过,在 Linux下, 文件名不在 inode 结构中保存?为什么?

那 文件名存储在哪里呢?存储在目录文件!

问题三:目录是文件吗?如何理解?



目录文件

目录其实也是文件:目录文件

命令 ls -lid 目录文件名 :查看该目录文件属性信息,如该文件的 inode

在这里插入图片描述

不卖关子:目录文件的内容存储的是文件名和 inode 的映射关系

目录既然是文件,当然也就可以打开!!!!!



举例:在终端上打开一个目录文件

(1)命令 for i in {1..5}; do touch "test$i.c"; done :在当前目录创建 5 个 test1.c 、test2.c 这样命名的文件
在这里插入图片描述

(2)命令 vim . :查看当前目录文件

在这里插入图片描述

既然 目录也是文件,目录文件也要有对应的数据块的!

目录 = inode + data block = 属性 + 内容



文件名和 inode 是互为映射的


因为在 LinuxWindows 等操作系统中的文件目录下都不能存在同名文件

如何保证文件的命名唯一性呢,可以依靠 文件名 和 inode 的映射关系,inode 绝对是唯一的,相应的文件名也为唯一的。

  • 用户输入文件名,同时一般需要带上路径,系统就会在该路径下,寻找对应文件名,通过 文件名和 inode 的映射,找到该文件的唯一inode值进行文件磁盘访问的相关操作
  • 文件名单向映射唯一一个 inode 值,而 inode 值可以被多个文件名映射,这种机制是通过硬链接实现的!


为什么一个目录能有 读/写/执行 rwx 权限

到这里,你应该可以明白,为什么一个目录还能有 读、写、执行 权限

若一个目录没有读权限,你就不能访问该目录下的文件:就是因为目录文件本身内部存储着文件名和inode的映射关系,必须要读目录,才能拿到该映射关系,才能拿到 inode 才能访问该文件

若一个目录没有写权限,就无法在该目录下创建文件、删除文件、修改文件名…:就是因为目录文件本身用于存储文件名和inode的映射关系,必须要有写权限,才能将文件名和inode的映射关系写入该目录文件中,而 创建文件本质就是将新文件和inode的映射关系写入、删除文件本质就是删除文件和inode的映射关系、修改文件名本质就是修改了新文件名和inode的映射关系

若一个目录没有执行权限,就无法进入该目录:进不去该目录的本质是打不开该目录文件



问题:普通文件和目录文件在底层有区别吗?

在我们前面讲解的 磁盘文件系统中,是否会区分该文件是普通文件、还是目录文件吗?

不会,在底层一视同仁!

在底层的 磁盘文件系统中,文件的属性存储在 inode 中,文件的内容存储在 Block 中,普通文件和目录文件本质都是 属性 + 内容,因此不会区分文件类型,而是按部就班的直接存储属性和内容这样的数据

在这里插入图片描述




问题:为什么要进行文件名和 inode 的映射?


那为什么要设计 inode 和文件名进行映射呢,为什么要套这一层呢?

效率问题:若我们使用文件名表示一个文件,系统寻找文件时,则需要比对众多的文件名(字符串!),时间复杂度是 O(n)

而文件名映射的 inode ,则只需比较 数值类型的 inode 数字串,时间复杂度是 O(1)


其实系统管理用户也是通过:用户名和 User 编号的映射关系

命令 ls -l 可以看到文件的创建者

命令 ls -ln :带上一个 n ,表示能显示数字就显示数字

此时就能看到用户名在系统看来就是一串数字!

在这里插入图片描述

不只是这几个例子:还是什么 GID :组别ID…

系统会主动规避字符串,而是使用映射的数字,字符串是给人看的,数字对系统来说才是高效的!



重新理解 ls -l 命令


该命令本质上是打开当前目录的目录文件,遍历所有文件和 inode 的映射关系,通过 inode 值找到磁盘中该文件的 inode 结构体,返回该结构体中存储的文件属性信息,将这些文件信息和文件名拼接处理成字符串展示到屏幕上,下图中文件名前面的都是该文件存储在 struct inode 中的文件信息!

在这里插入图片描述

可是问题又来了!

找到文件名 -> 首先要打开当前目录 -> 当前目录,也是文件!!也有文件名的呀!!

那目录的文件名如何被找到呢?

目录的目录的文件名又如何被找到呢?套娃呢

此时就需要 逆向的路径解析!



逆向路径解析

在这里插入图片描述

我如果需要访问 /lesson21 目录文件内容(如访问 test.txt 文件),就需要拿到目录文件 lesson21inode 找到磁盘中的相关的数据块,此时就要目录文件 /code 提供/lesson21 目录文件的 inode

我如果需要访问目录文件 /code ,就需要拿到目录文件 /codeinode 找到磁盘中的相关的数据块,此时就要目录文件 /112 提供/code 目录文件的 inode

一环一环,本级目录文件的内容访问,需要拿到 inode值,只能通过上级目录文件拿到该inode值

最终逆向回到根目录,而根目录是写死的,根目录文件的 inode值可以直接被获取然后访问对应数据块,相当于递归到出口了!!



正解:其实路径是被正向解析的,逆向只是为了方便理解路径需要被解析的!

系统其实是获取一个全路径,从根目录开始一次从左向右,依次解析路径的!!!



问题:为什么任何一个文件都要有路径?

就是要一环套一环的嵌套存储数据!!!!

没有路径,就根本不能直接找到该文件,只有通过一个全路径,对全路径进行一次解析解环,最终才能找到目标!



问题:为什么每个进程都要有一个 CWD ??

每个进程都有一个当前工作目录(CWD, Current Working Directory)主要是为了方便文件路径的引用。当你在命令行中运行程序或脚本时,很多时候需要访问或操作文件系统中的文件。这些文件可能位于不同的目录中,而使用相对路径来引用它们可以极大地简化这一过程。

当你启动一个新的进程时,它会继承其父进程的当前工作目录,除非特别指定了另一个工作目录。例如,当你从Bash启动一个程序时,这个程序默认的当前工作目录就是启动它的那个Bash进程的当前工作目录。这就是为什么即使是像Bash这样的祖先进程也需要维护一个CWD——它不仅用于自身的操作,也为所有由它启动的子进程提供了一个起点。


当前工作目录 CWD 最主要作用是用于形成相对路径

如下图,一个点 的意思是当前目录,两个点 的意思是上级目录

若你在当前目录下,打开当前目录的一个 log.txt 文件,只需要用当前目录的一个点 + 文件名的形式组成的路径,这就是相对路径!

open("./log.txt");

在该程序底层,表示当前目录的这个点,就会用 CWD 替换掉,如下:

// 假如 cwd 为 /root/code/"./log.txt"等于
"/root/code/log.txt" 



问题:路径需要被重复解析吗?

前面我们讲解了一个文件需要被系统做层层的路径解析,这个过程其实是不断的在访问磁盘,和磁盘进行IO交互的

/home/whb/code/code/112/code/1esson21

我们通过路径解析找到文件 lesson21,那如果我们还要查找访问当前路径下的 test1.c,难道我们还需要将该路径再次重复的解析一遍吗??

/home/whb/code/code/112/code/test1.c

并不需要,Linux系统会对路径进行缓存,解释如下:


路径缓存和缓存树 struct dentry 结构


路径缓存

在磁盘文件系统中是没有文件路径的概念的,如果有也是逻辑上的存在(路径本就是从物理层面抽象出来的一种逻辑化的概念)

磁盘中仅仅存在纯粹的 inode 和数据块,就是纯粹的存储文件及其数据内容

而我们操作系统中又是如何通过例如一个 tree 命令将整个系统的路径关系展示出来的呢?

是不是系统遍历了所有文件路径,然后不断和磁盘交互得来的,肯定不是,这样太慢了

其实,Linux系统会对路径进行缓存!,而且就是通过 多叉树这样的结构进行缓存的!!

在这里插入图片描述



缓存路径的多叉树并不会一次性将磁盘中的所有文件路径缓存下来,这个树展示的只是磁盘文件系统中的一小部分文件路径

除了一些基本文件和访问几率较大的文件会先被缓存下来,还有就是会缓存我们历史访问过的文件路径

如果下次还需使用到该路径,就直接查找文件路径缓存树即可

我们使用 find 命令查询文件时

比如在根目录下,按照名字查询目标文件:find / -name test.txt

find 命令查询文件也是要不断访问磁盘对应文件数据块内容

而我们说过,首次进行 find 命令查询时可能会比较慢,第二次之后就相对比较快了

这就是因为首次查询时,系统已经将访问过的文件路径缓存下来了,第二次之后的查询就不用过多的访问磁盘,只需查询路径树即可!!!



缓存树的 struct dentry 结构

Linux中,在内核中维护树状路径结构的内核结构体叫做: struct dentry


在这里插入图片描述




树的组成:一个 struct dentry 结构实际上算作一种树节点结构,多个 struct dentry 结构链接就形成所谓的 文件路径缓存树 !

struct dentry 结构:含有三个主要的字段

  • 父节点指针 :指向父亲 struct dentry 节点结构
  • 子节点指针 :指向孩子 struct dentry 节点结构
  • inode 值:本文件对应的 inode

每个文件都有dentry:每个文件其实都要有对应的 dentry 结构,包括普通文件。这样所有被打开的文件,就可以在内存中形成整个树形结构。

LRU 淘汰机制:整个树形节点也同时会隶属于 LRU(Least Recently Used),最近最少使用)结构中,进行节点淘汰。

整个树形节点也同时会隶属于Hash,方便快速查找。

更重要的是,这个树形结构,整体构成了Linux的路径缓存结构,打开访问任何文件,都在先在这棵树下根据路径进行查找,找到就返回属性inode和内容,没找到就从磁盘加载路径,添加dentry结构,缓存新路径。



在这里插入图片描述



例如我们需要通过该路径访问该 test 文件:/home/test
系统会在系统内存中的文件路径缓存 dentry 树中,根据给出的路径,正向解析,解析过程如下:
在这里插入图片描述

  1. 从根目录开始
    • 从根目录 /dentry 开始,根目录的 dentry 通常是内核中固定的。
    • 通过根目录的 dentry 获取根目录的 inode
  2. 查找 home 目录
    • 在根目录的 inode 中查找文件名 home 的目录项。
    • 如果找到 home 的目录项,获取 homeinode 号。
    • 系统为文件 home 创建一个 struct dentry 结构,并链接到文件缓存多叉树中。
  3. 查找 test 文件
    • home 目录的 inode 中查找文件名 test 的目录项。
    • 如果找到 test 的目录项,获取 testinode 号。
    • 系统为文件 test 创建一个 struct dentry 结构,并链接到文件缓存多叉树中。

这个过程少不了访问磁盘,因此路径解析一次通常会将文件路径缓存到文件缓存多叉树上,便于下次路径查询利用。



进程层面

这块内容记住就好:

通过查询源码,进程 task_struct 、文件 struct file 和 文件dentry 之间的关系如下

进程 struct task_struct 中存在文件描述符表 struct files_struct *file,该表中存储着文件描述符和文件 struct file 的映射关系,而每个加载到内存中的文件都会有对于的 struct file 结构,这个结构体中包含着很多和文件在内存中操作相关的字段属性,如

  • const struct file_operations *f_op 操作函数表、
  • struct address_space 内核文件缓冲区、
  • struct path *f_path 文件路径相关属性,指向一个path结构体
    • struct path 该path结构体中包含着 dentry 结构
      • dentry 结构:包含着不少的字段:
        • struct dentry *d_parent :指向父节点

        • struct list_head d_child:指向子节点

        • struct inode *d_inode:指向文件属性结构 inode


图示如下:下面有多张图片,选自己能看懂的即可

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述



题外话:其中,进程 task_structstruct fs_struct *fs 指向的 struct fs_struct 结构中,存储着 pwd,这就是为什么每个进程都有自己的 pwd

这个 pwd 是一种 struct path 路径结构,内部也存储着一个 dentry,当我们获取当前的 pwd 时,就是通过查询 pwddentry 的所处路径!
在这里插入图片描述



再次梳理:

目录项结构就是 struct dentry ,存储在上级目录文件的 inode 结构指向的数据块中

本目录文件目录项结构 struct dentry 中存储着本目录文件的 inode 结构指针,通过该指针找到自己的 inode 结构,进一步访问自己数据块的内容,内容中存储着自己这个目录文件存储的所有下级文件的目录项:下级文件名和其 inode 号的映射关系

路径解析核心是依次读取目录文件的 dentry ,并通过读取该 dentryinode 查找下一级目录或文件的目录项,并获取下级目录或文件的 inode 号。同时每次读取过的目录文件的 dentry 就会加载到内存中,并链接到文件缓存多叉树中,包括最终需要打开的文件,也是先要将该文件的 dentry 结构加载到内存中,然后才是通过dentry 结构访问 inode



完整流程


这是一次从路径解析到文件打开,再到文件读取的完整流程,目的是为了从具体的例子中梳理之前的学习内容,包括:文件打开需要的`struct file` 、路径解析、`inode` 结构等等综合知识点!
1. 路径解析

假设我们要访问路径 /home/test 并打开 test 文件,路径解析过程如下:

(1) 从根目录开始

  • 从根目录 /dentry 开始,根目录的 dentry 通常是内核中固定的。
  • 通过根目录的 dentry 获取根目录的 inode

(2) 查找 home 目录

  • 在根目录的 inode 中查找文件名 home 的目录项。
  • 如果找到 home 的目录项,获取 homeinode 号。
  • 为文件 home创建一个 struct dentry结构,并链接到文件缓存多叉树中。

(3) 查找 test 文件

  • whb 目录的 inode 中查找文件名 test 的目录项。
  • 如果找到 test 的目录项,获取 testinode 号。
  • 为文件 test创建一个 struct dentry结构,并链接到文件缓存多叉树中。


2. 文件打开

当文件 test 被打开时,内核会执行以下步骤:

(1) 获取 inode 结构

  • 通过 test 文件的 inode 号,从磁盘中读取 inode 结构并加载到内存中。
  • inode 结构包含文件的元数据,如权限、所有者、大小、数据块指针等。

(2) 创建 struct file 结构

  • 内核为 test 文件创建一个 struct file 结构。
  • struct file 结构包含文件描述符、文件操作指针、文件偏移量等信息。
  • struct file 结构中的 inode 指针指向 test 文件的 inode 结构。
  • struct file 结构中的 dentry 指针指向 test 文件的 dentry 结构。

到这一步,可以知道,其实文件的 struct dentry 结构,是在 struct file 结构之前创建的,当 struct file 结构创建后,内部的属性 dentry 指针才会指向本文件早已创建好的 dentry 结构!


(3) 返回文件描述符

  • 内核为 test 文件分配一个文件描述符,并将其返回给用户空间。
  • 用户空间通过文件描述符来操作文件。


3. 文件读取

当用户调用 read 系统调用读取文件内容时,内核会执行以下步骤:

(1) 查找数据块

  • 通过 test 文件的 inode 结构中的数据块指针,找到文件内容所在的数据块。
  • 数据块指针可能包括直接块指针、一级间接块指针、二级间接块指针等。

(2) 加载数据块到内存

  • 将数据块从磁盘读取到内存中,存放在内核的文件缓冲区中。

(3) 拷贝数据到用户缓冲区

  • 内核将文件缓冲区中的数据拷贝到用户提供的缓冲区中。
  • 用户可以通过 read 系统调用获取文件内容。

分区挂载

分区挂载

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[计算机网络]一. 计算机网络概论第一部分

作者申明&#xff1a;作者所有文章借助了各个渠道的图片视频以及资料&#xff0c;在此致谢。作者所有文章不用于盈利&#xff0c;只是用于个人学习。 1.0推荐动画 【网络】半小时看懂<计算机网络>_哔哩哔哩_bilibili 1.1计算机网络在信息时代的作用 在当今信息时代&…

Python操作Excel——openpyxl使用笔记(2)

2. 操作工作表 前面提到一个工作簿中会有一个或者多个工作表&#xff0c;当前使用的工作表被称作活动工作表&#xff0c;这里展开介绍一下对于工作表的一些操作。 2.1 枚举所有工作表 使用for循环可以很方便的遍历每个工作表&#xff1a; import openpyxl wb openpyxl.o…

Social LSTM:Human Trajectory Prediction in Crowded Spaces | 文献翻译

概要 行人遵循不同轨迹以避免障碍物和容纳同行者。任何在这种场景中巡航的自动驾驶车辆都需要能够遇见行人的未来位置并相应地调整其路线以避免碰撞。轨迹预测问题能够被看作一个顺序生成任务&#xff0c;其中我们对基于行人过去的位置预测其未来的轨迹感兴趣。根据最近RNN&am…

从零开始:Gitee 仓库创建与 Git 配置指南

引言 Git 是一款广泛使用的版本控制工具&#xff0c;它能够帮助开发者在开发过程中高效地管理代码的版本。而 Gitee&#xff08;码云&#xff09;是国内知名的 Git 托管平台&#xff0c;它提供了强大的代码托管、团队协作和项目管理功能。如果你是 Git 和 Gitee 的新手&#x…

挖掘机检测数据集,准确识别率91.0%,4327张原始图片,支持YOLO,COCO JSON,PASICAL VOC XML等多种格式标注

挖掘机检测数据集&#xff0c;准确识别率91.0%&#xff0c;4327张图片&#xff0c;支持YOLO&#xff0c;COCO JSON&#xff0c;PASICAL VOC XML等多种格式标注 数据集详情 数据集分割 训练组70&#xff05; 3022图片 有效集20&#xff05; 870图片 测试集10&…

CV 图像处理基础笔记大全(超全版哦~)!!!

一、图像的数字化表示 像素 数字图像由众多像素组成&#xff0c;是图像的基本构成单位。在灰度图像中&#xff0c;一个像素用一个数值表示其亮度&#xff0c;通常 8 位存储&#xff0c;取值范围 0 - 255&#xff0c;0 为纯黑&#xff0c;255 为纯白。例如&#xff0c;一幅简单的…

QT 使用QTableView读取数据库数据,表格分页,跳转,导出,过滤功能

文章目录 效果图概述功能点代码分析导航栏表格更新视图表格导出表格过滤 总结 效果图 概述 本案例用于对数据库中的数据进行显示等其他操作。数据库的映射&#xff0c;插入等功能看此博客框架&#xff1a;数据模型使用QSqlTableModel&#xff0c;视图使用QTableView&#xff0…

UI自动化测试:异常截图和page_source

自动化测试过程中&#xff0c;是否遇到过脚本执行中途出错却不知道原因的情况&#xff1f;测试人员面临的不仅是问题的复现&#xff0c;还有对错误的快速定位和分析。而异常截图与页面源码&#xff08;Page Source&#xff09;的结合&#xff0c;正是解决这一难题的利器。 在实…

Spark常见面试题-部分待更新

1. 简述hadoop 和 spark 的不同点&#xff08;为什么spark更快&#xff09; Hadoop是一个分布式管理、存储、计算的生态系统&#xff0c;包括HDFS&#xff08;分布式文件系统&#xff09;、MapReduce&#xff08;计算引擎&#xff09;和YARN&#xff08;资源调度器&#xff09;…

HackMyVM-Klim靶机的测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、信息搜集 2、Getshell 3、提权 CVE-2008-0166 四、结论 一、测试环境 1、系统环境 渗透机&#xff1a;kali2021.1(192.168.159.127) 靶 机&#xff1a;debian(192.168.159.27) 注意事…

数据结构题目 课时6

题目 1、设一棵树的度是 4&#xff0c;其中度为 0, 1, 2, 3, 4 的结点个数分别是 8, 4, 2, 1 和&#xff08; &#xff09;。 A. 4 B. 3 C. 2 D. 1 2、设一棵 m 叉树中有 N₁个度数为 1 的结点&#xff0c;N₂个度数为 2 的结点&#xff0c;……&#xff0c;Nₘ个度…

Linux下源码编译安装Nginx1.24及服务脚本实战

1、下载Nginx [rootlocalhost ~]# wget -c https://nginx.org/download/nginx-1.24.0.tar.gz2、解压 [rootlocalhost ~]# tar xf nginx-1.24.0.tar.gz -C /usr/local/src/3、安装依赖 [rootlocalhost ~]# yum install gcc gcc-c make pcre-devel openssl-devel -y4、 准备 N…

Web前端------表单标签

一.表单标签介绍 1.认识表单 表单---类似于日常生活中的申请单 都是去填写一些信息去申请某个功能&#xff0c;例如&#xff1a;账号密码昵称&#xff0c;登陆网站 2.常见标签 常见的标签 <form></form> 表单标签&#xff0c;所有表单信息都包含在这个标签内…

LLM - 大模型 ScallingLaws 的迁移学习与混合训练(PLM) 教程(3)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/145212097 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Scalin…

Unity2017 控制.abc格式的三维动画播放

首先需要导入插件Alembic&#xff0c;否则导入abc动画&#xff0c;Unity是不会识别的。 Unity2017版本及以下直接从我这儿下载&#xff1a;https://download.csdn.net/download/qq_41603955/90272382 高版本Unity&#xff0c;请移步AssetStore商店搜找。 导入abc之后&#x…

【视觉惯性SLAM:十七、ORB-SLAM3 中的跟踪流程】

17.1 跟踪流程流程图 ORB-SLAM3 的跟踪模块是整个系统的重要组成部分&#xff0c;负责实时确定相机在三维空间中的姿态位置&#xff0c;并保持关键帧之间的连续性。其基本目标是将输入的视频流与已有地图数据进行对齐&#xff0c;完成位姿估计和地图更新。 流程图概述 一个…

开发神器之cursor

文章目录 cursor简介主要特点 下载cursor页面的简单介绍切换大模型指定ai学习的文件指定特定的代码喂给ai创建项目框架文件 cursor简介 Cursor 是一款专为开发者设计的智能代码编辑器&#xff0c;集成了先进的 AI 技术&#xff0c;旨在提升编程效率。以下是其主要特点和功能&a…

CSRF攻击XSS攻击

概述 ​在 HTML 中&#xff0c;<a>, <form>, <img>, <script>, <iframe>, <link> 等标签以及 Ajax 都可以指向一个资源地址&#xff0c;而所谓的跨域请求就是指&#xff1a;当前发起请求的域与该请求指向的资源所在的域不一样。这里的域指…

cuda + cudnn安装

1.安装CUDA Toolkit 在设备管理器&#xff08;此电脑–右键–属性&#xff09;的显示适配器中可以查看自己的显卡型号&#xff0c;去下载对应的CUDA Toolkit 。或者输入以下命令查看Driver Version &#xff0c;cuda Version&#xff1a;12.2代表12.2版本以下兼容可以进行安装 …

vscode 设置

一、如何在vscode中设置放大缩小代码 1.1.文件—首选项——设置 1.2.在搜索框里输入“Font Ligatures”&#xff0c;然后点击"在settings.json中编辑" 1.3.在setting中&#xff08;"editor.fontLigatures":前&#xff09;添加如下代码 "editor.mous…