【Linux手册】操作系统如何管理存储在外设上的文件

发布于:2025-07-27 ⋅ 阅读:(17) ⋅ 点赞:(0)

前言

在前一篇文章中我们谈到了操作系统如何管理进程打开的文件,通过一个struct files_struct来对文件进行描述,组织和管理的。但是谈到的都是被打开的文件,那么操作系统对于没打开的文件又是如何管理的呢???

本文将侧重于Linux操作系统如何对没有打开的文件进行描述,组织和管理。

磁盘

没有被打开的文件放在那里???

对于没有被打开的文件不放在物理内存上,一定是放在外设上的,并且是具有存储功能的外设。到目前为止,一般笔记本和台式机上用于存储的就是SSD固态硬盘。但是此处我想聊一聊磁盘,磁盘是较早的存储介质,因为一些原因其在笔记本和台式上出现的频率大幅度下降,但是在服务器上仍然在大范围的使用。

磁盘是电脑上的唯一机械设备,磁盘是永久性介质,掉电数据不会丢失。
请添加图片描述

磁盘主要分为六个部分组成:马达,盘面,磁头,磁力臂,永磁铁,控制电路。

上图也可以看到,盘面其实不止一个,有多个盘面,每个盘面上都记录着数据,每个盘面都搭配一个磁头。

磁盘是如何读取,写入数据的

磁盘上的数据都是0,1序列,计算机只能读懂0和1。可以将盘面理解为由上亿个小吸铁石组成的,而因为每个小吸铁石都有南北极之分,就可以用南极表示0,用北极表示1的方式来存储0,1序列;而通过电池脉冲的方式可以将南北逆置,进而实现0,1序列的写入。

表示0和1的方式有很多,磁盘选取的是南北极的方式,其他存储介质就不一定了。

磁盘要如何对数据进行读取呢???数据都存储在盘面上,那么怎么从盘面上读取数据呢???

盘面的中间部分有马达可以带动盘面进行高速旋转,让磁头可以移动到一整个环的所有位置,磁力臂会带动磁头左右移动,让磁头能移动到盘面的所有位置。磁头并没有与盘面直接接触,但是磁头可以通过一定的方法获取盘面上的南北极信息,进而转换为0,1序列来实现数据的读取。

磁盘的存储构成

请添加图片描述

  • 扇区:磁盘访问时最基本的单位是扇区,扇区的大小一般是512字节(目前也有4kb的),也就是说从磁盘上拿或写时最少要512字节。
  • 磁道:以主轴为圆心的同心圆,就是磁道。可以理解为由扇区组成的圆环;
  • 柱面:因为盘面有一组,所以对于半径相同的同心圆也有一组,其整体被称为柱面。是由多个半径相同的磁道组成的;
  • 盘面:由所有磁道组成的。

因为磁盘访问的最基本的单位是扇区,所以在先磁盘中写入数据的时候,第一件事就是定位扇区,如何定位扇区呢???

  1. 确定柱面Cylinder:通过磁力臂带动磁头左右移动就可以定位要当问哪一个磁道;
  2. 确定磁头Header:用磁头来确定在哪一个盘面上,因为磁头较少,所以可以对磁头进行编号,这样通过编号与磁头一一对应,就可以确定磁头;
  3. 确定扇区Sector:通过主轴带动盘面旋转,使得磁头可以移动到磁道的所有位置,使得可以确定具体扇区。

这种确定位置的方式被称为:CHS寻址(Cylinder-Header-Sector,柱面,磁头,扇区)。

因为在进行数据的查找的时候需移动盘面和磁头,所以在进行数据的存储的时候,磁盘的设计者应该有意识的将有关的数据存储的更加集中,来减少数据的查找时间,进而提高效率。

磁盘的逻辑结构

在上面我们谈到了磁盘的存储结构,那操作系统是如何理解磁盘的呢?或者操作系统是以什么角度看待磁盘的呢???这就不得不对磁盘的结构进行抽象了。

我们可以将每一个磁道展开或将其拉成直的,如下图:
请添加图片描述
这不就是一维数组嘛!!!

而每一个同心圆的磁道又可以构成柱面,如下图:请添加图片描述

这不就是二维数组嘛!!!

再有多个柱面就可以形成磁盘的所有盘面了,如下图:请添加图片描述

最终可以使用三维数组来描述磁盘

再将上面的逻辑地址转化为线性地址就成了:
请添加图片描述

现在就成功的将磁盘抽象为了一个以扇区为基本单位的数组。

所以如果现在对每个位置进行编号,不就是的每个扇区都有了自己的地址位置。我们称这种地址叫做LBA地址,该地址是线性地址,是在依次递增的,与CHS地址天差地别。

操作系统通过LBA地址对确定磁盘上的具体空间位置的

操作系统使用LBA地址,而磁盘上使用的是CHS地址,那么这两种地址之间必定需要进行转换,如何进行转换???

数组的基本单位是扇区,而磁盘的基本单位也是扇区,所以:

  1. 如果知道一个磁盘上又多少个扇区就可以确定LBA地址对应的位置找哪一个扇区了:扇区编号即磁头的编号Header=LBA地址/(一个盘面上扇区的个数);
  2. 如果知道一个磁道上的扇区的个数,就能知道在哪一个磁道了:磁道编号=LBA地址%(一个磁盘上扇区的个数)/(一个磁道上扇区的个数);
  3. 确定磁道之后,那么剩余的地址就是扇区在磁道上的具体位置:扇区编号=LBA地址%(一个磁盘上的扇区个数)%(一个磁道上扇区的个数);

通过以上三步就能实现,将LBA地址先CHS地址的转换了。

补充:磁道的长度是不同的,扇区的大小是一样的,那么怎么保证每一个磁道上扇区的个数是一样的???

每一个磁道的周长不一样,但是可以通过控制每一个磁道存放0,1序列的密度来实现每一个磁道上扇区个数一样。现在的技术也可以实现对不同大小磁道的定位,只不过使用的算法不同而已。


文件系统

分治

因为磁盘太大了,所以操作系统直接管理起来比较难,所以在进行管理之前操作系统对先对磁盘进行分区,将整个磁盘进行划分,关于分区对操作系统来说处理起来并不难,只需要记录每一个区域对应数组的起始地址和结束地址就行了,在Window下分盘实际上就是分区操作。所以对于磁盘的管理就变成了对每个区域的管理。

在每个区域中又对存储的数据进行划分,将每个区划分成一个个的数据块Block group,进而对更小的区域进行管理就行了,类似于分治:先对小区域进行管理,每一个小区域管理好了,其整体部分也就管理好了。示意图如下:请添加图片描述

上图中启动块(Boot Block/Sector)的⼤⼩是确定的,为1KB,由PC标准规定,⽤来存储磁盘分区信息和启动信息,任何⽂件系统都不能修改启动块。启动块之后才是⽂件系统的开始。关于启动块此处不进行研究,我们重点关注后面的Block Group是用来干什么的。

Block Group

Block Group是用来对磁盘文件进行描述和管理的,文件=文件内容+文件属性,所以Block Group中必定有存储文件内容和文件信息的区域。实际上在磁盘上对于文件内容和文件属性的存储是分开的,所以Block Group内部一定也存在着各种区域的划分。Block Group的内部结构见下图:请添加图片描述

  • Date Block: 专门用来存放数据的,数据是以数据块的形式呈现的,一个数据块一般大小是4kb,Date Block中包含很多数据块。
  • Inode Table: inode就是专门用来存储文件属性的,==inode中包含单个文件的所有属性,==inode的大小一般是固定的,是128字节。对于每一个文件都有一个inode,并且还有每个inode都有唯一的编号进行标识。inode table用来存储当前Block Group中所有的inode。
  • Inode Bitmap: 是一个位图结构,用来标记每个inode是否有效,是否被使用了。
  • Block Bitmap: 也是一个位图结构,用来标记每一个Date Block中的数据块是否被使用。

Inode Bitmap和Block Bitmap都会在向磁盘中新增文件时使用,来为文件找位置。

补充:一个分区中有多少个Block Group是确定的,一个Block Group中有多少个inode是确定的,所以一个分区中有多少个inode也就是确定的。

inode在一个分区中的分配是从小到大的,也就是说:如果假设一个Block Group中有100个inode,那么Block Group 0中可以使用编号为0-99的inode,Block Group 1中可以使用编号为100-199的inode…

因为inode编号在分区中是依次递增的,所以在每个Block Group中不能直接将inode映射到inode Bitmap上,还需要减去该Block Group的起始inode编号

inode可以存储一个文件的所有属性,那他一定要能够存放文件在Date Block中占据的数据块位置,但是前面我们说过一个inode大小一般是128字节,而一个地址如果是4字节,那inode对应的文件最大不就只有128kb了吗???

上面的计算,默认了一个inode映射的地址就是数据块的地址。但是并非如此,在inode对于数据块的索引方式不只是采用直接索引,其还可以使用间接索引的方式。

请添加图片描述

在Linux中可以通过ls -i来打印当前目录下所有文件的inode信息:
请添加图片描述

也可以通过stat指令查看一个文件的属性:请添加图片描述

  • Super Block: 专门用来存放整个分区的各种信息:请添加图片描述
    Super Block中存放的是整个分区的信息,所以并不需要让所有Block Group都存放一份;如果Super Block失效了,那么操作系统就无法获取整个分区的完整信息了,就不能再使用这个分区了,所以在一个分区中会在多个Block Group中多个相同的Super Block来保证一个丢失了,操作系统还能使用该分区。
  • Group Description Table:存放该Block Group中个各种属性,比如Inode Bitmap,Block bitmap存放在那个数据块中…请添加图片描述

文件操作的本质

目录的本质

在对文件操纵的时候毫无疑问,需要向确定该文件的位置,而在Linux中我们使用路径来确定位置,而这毫无疑问离不开目录。关于目录此处将结合上面的文件系统谈谈。

Linux下一切皆文件,那么目录也是文件,那么目录也有自己对应的inode编号?那目录有没有内容呀,它的内容是什么?

Linux操作系统根本就不分目录和普通文件,操作系统认为他们都是文件,目录有自己的inode编号,通过ll -i也能看到。那么目录是有属性的,目录中的内容是什么?我好像从来没有向目录中写过内容呀? 确实,我们没有向目录中写过内容,目录中的内容都是由操作系统来写的,目录中的内容就是我们用ll指令显示的内容,目录中主要存储着目录中文件的inode和文件名称的映射关系。

那如果我要打开当前目录下的一个文件,操作系统就要先拿到该文件的inode,那操作系统必须先拿到当前目录的文件内容,也就说先拿到该目录的inode,那操作系统如何找到目录的inode呢???

目录的inode和文件名映射关系在上一级目录,所以要到上一级目录的文件内容中进行查找,那么上一级目录的又是怎么找到的呢,还是在上一级目录,所以一直往上递归,知道递归到根目录,只要知道根目录的inode就行了,根目录的inode一般是固定的为2;所以这就是为什么操作系统需要使用绝对路径才能定义一个文件在什么位置。

在上面一次次的递归查找的过程中,都要到查找目录的内容来看inode和文件名的映射关系,所以每一次都要读取磁盘上的数据,要多次与外设进行交互,而我们都知道与外设进行交互的效率很低。所以操作系统中有一个结构体struct dentry{}目录项缓存,其中存储着常用的文件的文件名和inode的映射关系,而缓存时内存级别的,读取就更快,效率也就更高了。
请添加图片描述

以路径 /home/user/test.txt 为例:

  1. 从根目录 / 的 dentry 开始(固定节点,始终缓存)
  2. 查找 / 的子目录 home: - 检查 dentry 缓存中是否存在 home 的 dentry - 若存在,直接获取对应 inode - 若不存在,读取磁盘上 / 的目录内容,创建 home 的 dentry 并缓存
  3. 重复上述步骤查找 usertest.txt 4. 最终通过 test.txt 的 dentry 获取文件的 inode,访问实际数据。

新建文件

当在Linux中新建一个文件操作系统会做些什么?

  1. 根据目录确定分区;
  2. 查看当前分区的Super Block超级块,看当前分区中inode和数据块够不够;
  3. 如果够为该文件分配inode,修改Inode Bitmap中的数据,将文件的基本属性写入inode中;
  4. 查看Block Bitmap为文件分配数据块,并在inode中记录文件占据那些数据块;

删除文件

当在Linux中删除一个文件操作系统会做些什么?

删除文件的本质并不是真的将数据删除,而是让存储数据的区域可以被其他数据覆盖。

  1. 先根据文件的inode查看该文件的数据都存储在那些位置,将这些块对应的Block Bitmap的位置都置为0,表示这些位置的数据是无效数据,可以被覆盖了;
  2. 将Inode Bitmap中的对应位置置为0,表示这个inode是无效的inode了,可以让后面新建的文件使用。

文件的删除只需要将对应位图中的位置修改,让这些区域的数据可以被覆盖,再次使用就可以了,这就是为什么删除文件很快的原因。


网站公告

今日签到

点亮在社区的每一天
去签到