【Linux系统】文件系统和软硬链接

前言

之前的博客介绍过了打开的文件是如何被操作系统管理起来的,但是绝大多数文件是没有被打开的,静静地躺在磁盘上。

这些文件也应该要被操作系统管理起来,以方便系统快速地在磁盘上查找它们,进而加载到内存。

这套管理方式就是文件系统,有了文件系统相关的强大知识储备后,理解文件的软硬链接简直小菜一碟。

本文先介绍磁盘这种外设是怎样寻址定位数据;接着介绍操作系统是如何将磁盘物理结构抽象成线性空间,方便统一管理;然后开始介绍文件系统:磁盘上各种类型的数据是如何组织的,操作系统是如何通过文件名在磁盘上查找文件的;最后介绍文件的软硬链接

全文篇幅较长,需要静下心来,变阅读边思考,相信你看完本文一定会有收获!!!

一.磁盘的物理结构

  1. 磁盘一般不只一个盘片,而是一摞
  2. 一个磁盘两面都会存储信息
  3. 每个盘面都对应一个磁头

对于一个盘面

  1. 分为若干个同心磁道(圆环)
  2. 一个磁道分为若干个扇区
  3. 每个扇区的大小(存储信息的能力)相同,都是512字节
  4. 扇区是磁盘的最小存储单元

注意:

  1. 每个扇区的存储能力相同,这可以通过调整盘面上小磁针的疏密排布达到目的 
  2. 扇区是磁盘的最小存储单元,意味着即使想要修改文件中1个字节的数据,也要将对应扇区的512字节数据加载到内存,修改后再将512字节数据刷新到磁盘,因此我们将磁盘这种支持随机读写,并且有最小存储单元的设备称为块设备

二.磁盘寻址方式——CHS定位法

想要找到一个扇区:

  1. 选择哪一个磁头(head)
  2. 选择哪一个磁道(cylinder)
  3. 选择哪一个扇区(sector)

磁道也可翻译成track,但是cylinder是专业名词,它本意是“圆柱”,因为相同半径的多个磁道构成一个圆柱,所以这是一个形象的比喻。 

磁盘常常采用CHS定位法(cylinder head sector)来确定一个扇区。按照上述我们所说的定位扇区的方法,应该叫做“HCS”才合理。事实上,这样命名也是有理由的。

磁盘有两个地方能做机械运动:机械臂杆和转轴(如图)。先通过机械臂杆摆动到一定位置确定若干个磁道(一个柱面),然后选择某个磁头(该过程不需要机械运动,只需电信号控制即可),最后旋转转轴使得磁头对应到某个扇区,这样就能对该扇区的内容进行读写了。简言之,磁头摆动确定柱面,盘面转动确定扇区

三.磁盘的逻辑结构

1.线性地址和物理地址互相转化

将磁盘盘片想象成线性空间,即将圆形的磁盘“拉直”,将整个磁盘抽象成以扇区大小为单位的数组,操作系统对磁盘的管理就变成了对以单元大小为扇区的数组的管理。

只需知道磁盘的规格大小,即可将线性地址(数组下标)转化成CHS地址

例如磁盘有4个盘面,每个盘面有10个磁道,每个磁道有100个扇区,现在操作系统想要访问下标为2002的扇区,则2002/(10*100)=2,第三面盘片;2/100=0,第1个磁道;第2个扇区。

2.LBA地址

操作系统可以按照扇区为单位进行存取,但更常用的是以文件块为单位进行数据存取。因为1个扇区比较小,IO效率比较慢,为了减少IO次数,文件系统规定以8个扇区为一个文件块(4KB),以文件块为单位进行IO,一个文件块就是保存文件属性或内容的基本单元。

给每个文件块从0开始进行编号,这个编号叫做LBA地址(逻辑块地址)。

同样LBA地址也能转化成CHS地址,操作系统对于磁盘的管理,就转化成了单元大小为4KB的数组的管理。

四.文件系统

1.磁盘分区和分组

磁盘容量一般较大,为了方便管理,将一个磁盘划分成多个区域,管理好一个区域,就能管理好多个区域,进而管理整个磁盘。实际上,我们使用的笔记本电脑追求轻薄易携带,一般只有一个磁盘,C,D,E,F盘是一个个磁盘分区。一个分区就是一个文件系统。

同样,还可以将一个分区划分为若干个块组(Block group),想要管理好整个磁盘,只需管理好一个块组,这是一种分治的思想

2.块组的结构

在说块组之前,先介绍Boot block,第一个分区的开头才有这个启动块,在磁盘上对应第一个磁头的第一个磁道上的第一个扇区,它和计算机启动有关。其中包含了磁盘的分区信息,操作系统在磁盘上的地址。计算机启动时BIOS会读取Boot block,然后加载操作系统。如果Boot block出错,计算机就无法启动。

(1)块组存储的信息

一个块组要存储两类信息:

  1. 我的文件信息,包括文件内容和属性,这两项是分开存储的
  2. 文件管理的数据

并且根据常识,在文件信息被存储之前,一定要先让管理数据写入到块组当中。整个分区在被存储文件之前,要先将管理数据写入。而所谓的“格式化”,并不是清空分区的所有数据,而是将用户的文件信息全部清空,管理数据恢复到出厂设置。管理数据还有,所以该分区仍然能存储文件

(2)文件的inode

这一串数字就是文件的inode编号。

inode是一个结构体,一般情况下,一个文件一个inode,inode有自己的编号,叫做inode编号(inode的成员变量),inode编号从0开始逐1递增,在整个分区具有唯一性。

Linux内核中,识别文件只和inode编号有关,和文件名无关

(3)inode Table

一个文件的数据分为属性和内容,每个文件属性的种类是相同的,例如inode编号,文件大小,文件权限,文件ACM时间,创建者,所属组等。

文件的属性在inode中保存,每一个inode大小是相同的,都是128字节,所以一个文件块可以存储32个inode,操作系统一次性可以加载32个inode

并且inode的存储顺序是按inode编号从小到大排列的,因此只需知道inode编号,就可以知道相对于inode Table首地址的偏移量,进而找到对应的inode,拿到文件的所有属性。

不知道你是否发现了一个问题,inode编号在整个分区具有唯一性,而inode Table每个块组都有,怎么能根据inode编号确定相对于inode Table的偏移量呢?事实上,每个块组都保存了本组的inode Table的起始inode编号,只需用inode编号减去起始inode编号,就可以得到偏移量。

(4)Data Blocks

文件的属性在inode中,而内容存储在Data Blocks中。文件系统的基本存取单元是文件块,也即4KB,所以Data Blocks也是由一个个4KB的单元组成的,这些单元叫做数据块(Data block)。一个文件如果有内容,即使只有1个字节,也要占据一个数据块。

我们将这些数据块从0开始编号,这些编号就可以标识一个个数据块。inode中有一个非常重要的属性,即文件内容的存储位置。int blocks[15],里面存储着文件内容所在的数据块编号,根据这一数组即可找到文件内容。

因此,文件的属性和内容就关联起来了。

tips:blocks映射的数据块最多只有15个吗?no!0~12号是直接映射;13号是二级映射,即13号映射的数据块中存储的是数据块的编号;14号是三级映射。如果一个块组都放不下了,就要涉及到跨组访问,记录好下一个块组的编号和存放的块号。

小结:操作系统有文件的inode编号,根据每个块组的起始inode编号,确定inode编号所在区间以定位块组;然后将inode编号减去该块组的起始inode得到偏移量,进而在inode Table中找到inode,得到文件属性;然后根据blocks数组找到文件内容所在的数据块,得到文件内容

(5)inode Bitmap

当你创建新文件时要分配新的inode编号,可是操作系统如何知道inode Table中哪些inode是有效的,哪些是未分配的呢?这时就需要用一种数据结构来记录inode的分配情况了。

假设inode Table的大小是1000KB,总共就有32000个inode。使用位图来标识inode的分配情况,第m个比特位为1,表示第m个inode已经分配了,为0表示没有被使用。该位图一个文件块大小就够用了。

想要为新文件分配inode,只需在inode Bitmap中查找哪个比特位是0,该比特位的位置就是偏移量,找到后在inode Table中为文件创建inode,然后将inode编号返回给操作系统即可

(6)Block Bitmap

和inode Bitmap类似,每个比特位的位置表示数据块编号,比特位的内容表示数据块是否被使用。Block Bitmap记录数据块的使用情况

小结:新建一个文件,先查询inode Bitmap,找到最近一个为0的比特位,将该比特位由0置1,该比特位的位置就是偏移量,根据偏移量在inode Table中找到inode,将文件属性写入inode。如果文件的内容需要2个块,在Block Bitmap中找到2个为0的比特位,将其置1,比特位的位置就是数据块编号,然后在inode中的blocks数组中添加编号,最后向相应编号的数据块中写入文件的内容,将inode编号返回给上层。

删除一个文件,根据inode编号定位文件所在的块组,将该块组的inode Bitmap和Block Bitmap中相应的比特位清0即可(找比特位的方式是上面的逆过程)。 

因此,删除文件只是对两个位图进行操作,标识文件的属性和内容所在的存储空间是空闲的,但是内容还没有被覆盖。所以误删文件后不要新创建文件,防止文件信息被覆盖,只需知道文件的之前的inode,就有办法通过更改位图的方式来恢复文件。

(7)Group Descriptor Table(GDT)

保存整个块组的信息,例如块组的起始inode编号,整个块组的inode/数据块数量,已经被使用的inode/数据块数量,下一次要被使用的inode编号,块组的大小等

 以上5个块是每个块组都有的,但是Super Block块组只有几个块组拥有

(8) Super Block

存放文件系统(分区)本身的结构信息。记录的信息主要有:文件系统的名字,数据块和inode的总量,未使用的数据块和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息

Super Block的信息被破坏,可以说整个文件系统结构就被破坏了,所以在多个块组中安排Super Block以备份文件系统信息,以备不时之需。

操作系统在内核中维护一张文件系统列表,即在开机时将每个分区的Super Block加载到内存,管理多个文件系统,只需管理多个Super Block

3. 文件名和inode编号

操作系统通过inode编号识别一个文件,但是用户使用的是文件名,因此文件名和inode存在映射关系,这种映射关系存储在文件所在的目录中。

目录也是文件,它也有inode,也有内容和属性,目录的内容是就是目录里的文件的文件名和inode的映射关系。这里的文件包括目录,因为目录也是文件。

所以同一个目录下不允许存在同名文件,因为文件名被当作查询inode时的key值

前面讨论过,在操作系统的层面删除一个文件是要更改两个位图,因为操作系统只认inode编号。而在用户层,我们通过文件名来识别文件,删除文件实际上是在文件所在目录中删除文件名和inode编号的映射关系。

所以,文件名不属于文件属性,inode中没有文件名,文件名存储在目录中

4.操作系统通过文件名在磁盘上查找文件的方式

例如我想找到file.txt在磁盘上对应的文件,一定要先找到file.txt所在的目录dir,打开目录文件dir,找到它的内容,得到文件名和inode的映射关系,得到file,txt的inode编号,定位块组,找到inode,找到inode中记录的数据块,得到文件的属性和内容。

问题在于,如何找到dir呢?想要找到dir,就要知道它的inode编号,那就要找到dir的父目录,找到父目录的父目录……而一切都要从根目录“/”开始找起,因为根目录的inode编号是固定是2。

因此我们要找到一个文件,只需要文件的路径,即一级一级的目录,然后就能从根目录开始一层一层地往下找,最终在磁盘上找到该文件。那么路径是谁给的呢?答案是用户或者进程。一个进程在启动时所在的路径会被记录下来(cwd),用户如果传了路径就使用用户传的,如果没传,就使用进程启动时所在的路径。如果用户传递了一个错误的路径,进程就会报错,因为操作系统在解析路径时,在目录文件中没有找到相应的映射关系。

5.挂载分区 

一个磁盘被分区格式化后,Linux想要使用这个分区,就要把这个分区进行挂载mount。意思是将这个分区与某个目录进行关联,操作系统只要确定你在访问这个目录,就到对应的分区上去寻找文件。每一个文件都有路径,可以通过路径的前缀判断出我们的文件在哪一个分区下

使用df -h命令查看分区的挂载情况,可以看到我使用的机器只有一个分区vda1,这个分区被挂载到根目录

总结:CPU执行int fd = open("./log.txt", "r")语句,操作系统做了哪些工作?

log.txt是由进程打开的,进程有自己的cwd,再结合我们传入的路径,就能组成一个以根目录为起点的路径。根据路径前缀,结合分区的挂载情况确定该文件在那个分区下。

文件名和inode编号的映射关系存储在目录文件中,从根目录开始,一层层地打开目录文件,找到文件和inode编号的映射关系,最终找到log.txt的inode编号。

根据每个块组的起始inode编号,确定inode所在块组,用inode编号减去inode Table的起始inode编号得到偏移量,找到inode Table中的inode,将inode加载到内存。在内存中构建struct file结构体,将inode的属性填充到struct file结构体中,同时让struct file 指向内存级inode。

然后根据inode的blocks数组找到相应数据块,将数据块中的文件内容加载到struct file的内核缓冲区。

五.文件的软硬链接

1.软硬链接区别

ln -s log log.soft.link  给log建立软链接log.soft.link

 ln hello hello.hard.link  给hello建立硬链接hello.hard.link

 通过对比inode编号,可以得出结论:

软链接是一个新的文件,因为操作系统为它分配了新的inode。硬链接在操作系统层面不是一个新文件,因为没有为它新分配inode。

2.软硬链接的实质

软链接类似于windows的快捷方式,是独立文件,有独立的inode,软链接的内容是指向的目标文件的路径

硬链接不是一个独立文件,本质是在指定目录内的一组文件名和inode编号的映射关系。在操作系统层面,硬链接和目标文件是同一个文件,因为访问硬链接和目标文件是访问同一个inode。

3.文件的删除 

这一串数字是文件的硬链接数(实质稍后会讲) 

删除一个其中一个(删除链接文件用rm和unlink均可),会发现另一个文件的链接数减少了

 inode中包含一个int ret_count的引用计数,有多少个文件名映射它,引用计数就是多少,当没有文件名映射它时,操作系统就会将该文件删除。

所以用户层面的删除文件,是在父目录中删除文件名和inode的映射关系,当一个inode没有文件名指向它时,操作系统就会删除该文件,即更改两个位图。

文件的硬链接数就是inode中的引用计数

 4.当前目录.和上级目录..

分别新建一个普通文件和一个目录文件,普通文件的硬链接数是1,目录文件的硬链接数却是2,为什么是2呢?任何一个目录都会包含两个隐藏的目录文件.和..

.就是dir目录本身,通过对比也可以发现,它们的inode是相同的,所以dir的硬链接数为2 

在dir目录下再新建一个newdir目录,惊奇地发现,dir的硬链接数变成了3

 这是因为newdir下的隐藏目录..的inode编号和dir的相同。

5.用户无法对目录建立硬链接 

通过实践证明无法对目录建立硬链接。为什么呢?

因为当我们使用某些查找指令例如find时,需要指定开始查找的路径,而硬链接会导致路径成环,查找时深度优先遍历会无穷递归。而目录是可以建立软链接的,虽然它也会成环,但是软链接是一个普通文件,find时只会进入目录文件,不会cd到普通文件

操作系统不允许用户对目录建立硬链接,但是它自己对每个新建的目录都 创建了.和..两个硬链接,这是特殊处理,find时不会进入这两个目录。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue使用json格式化

安装 npm i bin-code-editor -S // Vue2 npm install vue-json-viewer --save 在main.js引用 //引入bin-code-editor相关插件和样式 import CodeEditor from bin-code-editor; import bin-code-editor/lib/styles/index.css; import JsonViewer from vue-json-viewer //vue使用…

golang开源的可嵌入应用程序高性能的MQTT服务

golang开源的可嵌入应用程序高性能的MQTT服务 什么是MQTT? MQTT(Message Queuing Telemetry Transport)是一种轻量级的、开放的消息传输协议,设计用于在低带宽、高延迟或不可靠的网络环境中进行通信。MQTT最初由IBM开发&#xf…

python webdriver 测试框架数据驱动json文件驱动的方式

简介: 数据驱动excel驱动方式,就是数据配置在excel里面,主程序调用的时候每次用从excel里取出的数据作为参数,进行操作, 需要掌握的地方是对excel的操作,要灵活的找到目标数据 测试数据.xlsx: 路径-D:\test\0627 E…

产品原型图设计规范大全

目前,市场上许多产品经理或设计师都在使用一些优秀的原型设计规范,这些规范几乎涵盖了原型设计的许多方面。一套好的、完整的原型设计规范可以统一产品设计风格,检验产品的可用性,有效提高产品经理绘制原型图的效率,更…

力扣238. 除自身以外数组的乘积(前后缀和)

Problem: 238. 除自身以外数组的乘积 文章目录 题目描述思路复杂度Code 题目描述 思路 思路1: 1.先求取数组的包括当前下标值得前后缀乘积(利用两个数组记录下来分别为leftProduct和rightProduct) 2.当求取一个下标为i的数组中的元素&#x…

构建基于Flask的跑腿外卖小程序

跑腿外卖小程序作为现代生活中的重要组成部分,其技术实现涉及诸多方面,其中Web开发框架是至关重要的一环。在这篇文章中,我们将使用Python的Flask框架构建一个简单的跑腿外卖小程序的原型,展示其基本功能和实现原理。 首先&…

linux --中断管理 -- irq的自动探测机制

irq自动探测机制 如果一个设备的驱动程序无法确定它说管理的设备的软件中断号irq,此时设备驱动程序可以使用irq的自动探测机制来获取其正在使用的irq。 使用自动探测机制的条件 内核与驱动,必须共同努力才能完成只限于非共享中断的情况 探测前&#…

如何查看某一页面在在谷歌有哪些关键词

随着跨境贸易的不断发展,谷歌SEO也被越来越多的人群所了解,所接受。我们在日常操作SEO的时候,往往都会远见这样的事情,那就是自己网站的某一个页面原本只是简单的承载着某一个关键词,但是随着时间的推移,这…

Shell脚本之 -------------免交互操作

一、Here Document 1.Here Document概述 Here Document 使用I/O重定向的方式将命令列表提供给交互式程序 Here Document 是标准输 入的一种替代品,可以帮助脚本开发人员不必使用临时文件来构建输入信息,而是直接就地 生产出一个文件并用作命令的标准…

Linux——动静态库

在进行开发过程中,我们不可避免地会使用到人家的库,那么库到底是什 么?而库又分为动态库和静态库,那么这两个又是什么?这篇博客由我来 简单介绍动静态库。文章目录 1. 库2. 静态库a. 静态库的制作b. 使用静态库 3. 动态…

打击者H5小游戏

欢迎来到程序小院 打击者 玩法&#xff1a;点击飞机上下左右移动躲过子弹射击&#xff0c;打掉上方敌人飞机&#xff0c; 遇到药包会增加能量&#xff0c;弹药包会升级武器&#xff0c;快去射击吧^^。开始游戏https://www.ormcc.com/play/gameStart/262 html <div id"…

基于矢量控制的交流电机驱动simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 交流电机基础 4.2 矢量控制原理 4.3 矢量控制的实现 5.完整工程文件 1.课题概述 基于矢量控制的交流电机驱动simulink建模与仿真。系统仿真输出电压&#xff0c;电流&#xff0c;电机转速以及扭矩…

语言革命:NLP与GPT-3.5如何改变我们的世界

文章目录 &#x1f4d1;前言一、技术进步与应用场景1.1 技术进步1.2 应用场景 二、挑战与前景三、伦理和社会影响四、实践经验五、总结与展望 &#x1f4d1;前言 自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;是人工智能领域的一个重要分支…

快速入门存内计算—助力人工智能加速深度学习模型的训练和推理

存内计算&#xff1a;提高计算性能和能效的新技术 传统的计算机架构是将数据存储在存储器中&#xff0c;然后将数据传输到计算单元进行处理。这种架构存在一个性能瓶颈&#xff0c;即数据传输延迟。存内计算通过将计算单元集成到存储器中&#xff0c;消除了数据传输延迟&#…

中国的茶文化:现代生活中的茶文化

中国的茶文化&#xff1a;现代生活中的茶文化 引言 在现代社会的快节奏生活中&#xff0c;茶文化并未随时间流逝而褪色&#xff0c;反而以其独特的方式融入了全球各地人们的日常生活。它超越了饮品本身的范畴&#xff0c;成为一种连接历史、人文与现代生活方式的艺术形式。本文…

Git 介绍 与 配置

Git 介绍 Git是一个分布式版本控制系统&#xff0c;用于跟踪文件的更改和协作开发。它可以管理项目的版本历史记录&#xff0c;并允许多个开发者在同一时间进行并行开发。 解决上图产生的问题就出现了git 分布式版本控制系统 看下图 Git 配置 Git的基本配置包括用户名和电子邮…

Linux split命令 切割文件

目录 一. 主要配置项二. 按照行数切割文件三. 按照指定大小切割文件 一. 主要配置项 ⏹将文件按照行数或者大小切割为若干份小文件&#xff0c;主要作用就是用来切割文件 -l&#xff1a;表示将文件按照行分割-d&#xff1a;表示使用数字作为分割后的文件名后缀, 而不是默认的…

BUUCTF-Real-[ThinkPHP]5-Rce

1、ThinkPHP检测工具 https://github.com/anx0ing/thinkphp_scan 漏洞检测 通过漏洞检测&#xff0c;我们发现存在rce漏洞&#xff01; 2、漏洞利用 ---- [!] Name: Thinkphp5 5.0.22/5.1.29 Remote Code Execution VulnerabilityScript: thinkphp5022_5129.pyUrl: http://n…

跟着cherno手搓游戏引擎【16】Camera和Uniform变量的封装

相机封装&#xff1a; OrthographicCamera.h: #pragma once #include <glm/glm.hpp> namespace YOTO {class OrthographicCamera{public:OrthographicCamera(float left,float right , float bottom,float top);const glm::vec3& GetPosition()const { return m_Pos…

阿赵UE学习笔记——13、贴花

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的使用。这次介绍一种特殊的材质类型&#xff0c;贴花。 一、获取贴花资源 在没有分析贴花的原理之前&#xff0c;可以先去获得一些免费的贴花资源来使用&#xff0c;比如在Quixel上面就有专门的一个资源…
最新文章