【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时不会进入这两个目录。

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值