文件系统详解

目录

1.理解硬件

1.磁盘的物理和存储结构

2.磁盘的逻辑结构

3.LBA和CHS的互相转化

2.文件系统

3.ext2文件系统

1.构成架构

2.inode和datablock映射

3.补充

4.目录和文件名

5.挂载分区

4.软硬链接

1.硬链接

2.软链接

3.两者对比和补充


1.理解硬件

首先,文件是存储在硬件磁盘里的,在Linux中,一切都是文件,所以我们要首先了解磁盘。机械磁盘是计算机中唯一的一个机械设备,它属于外设,特点是容量大,价格便宜,但是用起来比较慢。

1.磁盘的物理和存储结构

我们可以看上面的图,一个磁盘实际上有很多的片片,而每一片的正反面都可以用来存放数据。其中,每个磁道被分为若干个扇区,扇区是磁盘存储数据的基本单位,512字节,块设备。

这里上面的图要注意磁道,柱面的概念。

越靠近圈的中心的扇区越小,存储的越为密集,越远离中心的扇区面积越大,存储的就比较松散,这是为了保证每个扇区存储的数据量相同都为512字节。但是也有的磁盘不会限制磁道的固定存储,而是统一存储密度,这样方便制造,也可以根据需求调整磁盘容量。

如何定位一个扇区呢?可以先定位磁头,确定磁头要访问哪一个柱面(磁道), 定位一个扇区,这也是CHS地址定位。

扇区是从磁盘读出和写入信息的最小单位,通常大小为 512 字节。

磁头(head)数:每个盘片一般有上下两面,分别对应1个磁头,共2个磁头。

磁道(track)数:磁道是从盘片外圈往内圈编号0磁道,1磁道...,靠近主轴的同心圆用于停靠磁头,不存储数据。

柱面(cylinder)数:磁道构成柱面,数量上等同于磁道个数。

扇区(sector)数:每个磁道都被切分成很多扇形区域,每道的扇区数量相同。

圆盘(platter)数:就是盘片的数量。

磁盘容量=磁头数×磁道(柱面)数×每道扇区数×每扇区字节数。

需要注意的细节是传动臂上的磁头是共进退的。

CHS寻址对早期的磁盘非常有效,知道用哪个磁头,读取哪个柱面上的第几扇区就可以读到数据了。 但是CHS模式支持的硬盘容量有限,因为系统用8bit来存储磁头地址,用10bit来存储柱面地址,用6bit来存储扇区地址,而一个扇区共有512Byte,这样使用CHS寻址一块硬盘最大容量为256*1024*63*512B=8064MB(1MB=1024KB=1048576B)(若按1MB=1000000B来算就是 8.4GB)

2.磁盘的逻辑结构

磁带上面可以存储数据,我们可以把磁带"拉直",形成线性结构,那么磁盘本质上虽然是硬质的,但是逻辑上我们可以把磁盘想象成为卷在一起的磁带,那么磁盘的逻辑存储结构我们也可以类似于:

这样每一个扇区,就有了一个线性地址(其实就是数组下标),这种地址叫做 LBA :

柱面是一个逻辑上的概念,其实就是每一面上,相同半径的磁道逻辑上构成柱面。 所以,磁盘物理上分了很多面,但是在我们看来,逻辑上,磁盘整体是由"柱面"卷起来的。

所以,磁盘的真实情况是: 磁道: 某一盘面的某一个磁道展开:

即:一维数组

柱面: 整个磁盘所有盘面的同一个磁道,即柱面展开,每个柱面的扇区数一样,也可以看作二维数组:

整盘可以想成三维数组:

所以,寻址一个扇区:先找到哪一个柱面,再确定柱面内哪一个磁道(其实就是磁头位置),再确定扇区,所以就有了 CHS寻址 。我们之前学过C/C++的数组,在我们看来,其实全部都是一维数组:

所以,每一个扇区都有一个下标,我们叫做 LBA(Logical Block Address) 地址,其实就是线性 地址。

3.LBA和CHS的互相转化

CHS转成LBA:

LBA=柱面号C*单个柱面的扇区总数+磁头号H*每磁道扇区数+扇区号S-1

即:LBA=柱面号C*(磁头数*每磁道扇区数)+磁头号H*每磁道扇区数+扇区号S-1

磁头数*每磁道扇区数=单个柱面的扇区总数

扇区号通常是从1开始的,而在LBA中,地址是从0开始的

柱面和磁道都是从0开始编号的

总柱面,磁道个数,扇区总数等信息,在磁盘内部会自动维护,上层开机的时候,会获取到这些参数。

LBA转成CHS:

柱面号C=LBA//(磁头数*每磁道扇区数) 【就是单个柱面的扇区总数】

磁头号H=(LBA%(磁头数*每磁道扇区数))//每磁道扇区数 【//是整除】

扇区号S=(LBA%每磁道扇区数)+1

如果看文字感觉有点困难,我们可以直接"鬼脑发动",我们可以想象,LBA实际上是所有扇区排序之后的下标数,然后我们要得到它可以首先看在第几柱面,前面的柱面是满的,然后同一柱面有许多磁头,或者说我们要确定定位的扇区在哪个磁道上,然后再在找到的磁道上找到对应的扇区号,这不就是CHS计数的三个坐标吗。可以确定的是,单独满柱面的扇区数>单独满磁道的扇区数>扇区所在磁道的扇区下标。所以我们才可以做出整除的操作。

2.文件系统

其实硬盘是典型的 "块" 设备,操作系统读取硬盘数据的时候,其实是不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个 "块"(block)。硬盘的每个分区是被划分为一个个的 "块"。一个 "块" 的大小是由格式化的时候确定的,并且不可以更改,最常见的是4KB,即连续八个扇区组成⼀个 "块"。 "块" 是文件存取的最小单位。

我们在知道块的概念之后,也可以对块进行计算,每8个为一块,则计算扇区在第几块上就可以LBA//8,而LBA也可以通过块号*8+扇区在块中的位置来计算。

一个块实在是太小了,所以又有了分区的概念。其实磁盘是可以被分成多个分区(partition)的,以Windows观点来看,你可能会有一块磁盘并且将它分区成C,D,E盘。那个C,D,E就是分区。分区从实质上说就是对硬盘的一种格式化。但是Linux的设备 都是以文件形式存在,那是怎么分区的呢?柱面是分区的最小单位,我们可以利用参考柱面号码的方式来进行分区,其本质就是设置每个区的起始柱面和结束柱面号码。此时我们可以将硬盘上的柱面(分区)进行平铺,将其想象成一⼀个大的平面,如下图所示:

柱面大小一致,扇区个位一致,那么其实只要知道每个分区的起始和结束柱面号,知道每一个柱面多少个扇区,那么该分区多大,其实和解释LBA是多少也就清楚了。

我们说过文件=数据+属性 ,我们使用 ls -l 的时候看到的除了看到文件名,还能看到文件元数据(属性):

上面是一个示例,其中,从左到右除去第一列,依次是模式,硬链接数,文件所有者,文件所属组,大小,最后修改时间,文件名。其中,软硬链接作者会在本文的最后讲到。

文件数据都储存在块中,那么很显然,我们还必须找到一个地方储存文件的元信息(属性信息),比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为索引节点。我们想要查看一个文件的indeed就需要在 ls 的后面跟 -i 选项,如下图所示:

上面的文件权限前面的数为inode编号。每一个文件都有唯一对应的inode编号,并且实际上inode也是一种类,里面存放了文件的属性和一些其他的数据。

• Linux下文件的存储是属性和内容分离存储的

• Linux下,保存文件属性的集合叫做inode,一个文件就有一个inode,inode内有一个唯一 的标识符,叫做inode号

• 文件名属性并未纳入到inode数据结构内部

• inode的大小一般是128字节或者256字节

• 任何文件的内容大小可以不同,但是属性大小一定是相同的

3.ext2文件系统

1.构成架构

我们想要在硬盘上储文件,必须先把硬盘格式化为某种格式的文件系统,才能存储文件。文件系统的目的就是组织和管理硬盘中的文件。在 Linux系统中,最常见的是ext2系列的文件系统。其早期版本为ext2,后来又发展出ext3和ext4。ext3和ext4虽然对ext2进行了增强,但是其核心设计并没有发生变化,作者这里仅讲解ext2系统。

我们前面说了,每个分区都是按块来计算的,或者说最小单位是块--4KB,而分区管理文件又可以看下图:

上图中Disk那一栏是分区,但是分区其实也有些大,不太好管理,所以每个分区会由两部分构成,就是我们的第二层,其中EXT2File System则是存储文件,然后又会分组,就到了第三层,这个时候,分出来的第三层的每个组就是我们重点要了解的了,每个组是结构体,或者说这上面基本上都是结构体,一个Group结构体包含许多成员,可以看下图:

上面这些成员都是由4KB大小的块组成的,每个成员都由若干个块组成,块则是8个扇区组成(1扇区512字节,8个合起来就是4KB),真的是层层叠叠容易绕晕。我们都知道,文件=内容+属性,那么Data Blocks就是存储的文件的内容的那一部分,而 Inode table就是存储的文件的属性。这里需要特别记住文件名并不作为文件的属性存放在 Inode table 中,这一点我们后面再详细说明。而 Block Bitmap 和 inode Bitmap 则是作为位图来分别记录Data Blocks和 Inode table的每个块的使用情况。什么是位图呢,一个字节8个比特位,每一个比特位都会记录一个块的使用情况,如果这个块被使用了,那么对应比特位存的是1,反之则存0,这样的集合就是位图。那么一个块大小的位图可以记录多少块的使用情况呢?一个块=4KB=4*1024字节=4*1024*8比特位=32768比特位。也就是说,一个块大小的位图就可以记录32768个块的使用情况,非常神奇。

剩下的两个成员里,GDT是块组描述符表,描述块组属性信息,整个分区分成多个块组就对应有多少个块组描述符。每个块组描述符存储一个块组的描述信息,如在这个块组中从哪里开始是inode Table,从哪里开始是Data Blocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有一份拷贝。

最后一个Super Block是超级块,存放文件系统本身的结构信息,描述整个分区的文件系统信息。记录的信息主要有:bolck和inode的 总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息如果被破坏,可以说整个文件系统结构就被破坏了。超级块在每个块组的开头都有一份拷贝(第一个块组必须有,后面的块组可以没有)。为了保证文件系统在磁盘部分扇区出现物理问题的情况下还能正常工作,就必须保证文件系统的super block信息在这种情况下也能正常访问。所以一个文件系统的super block会在多个block group中进行备份,这些super block区域的数据保持一致。

2.inode和datablock映射

上面讲了,inode table里面存放的是文件的属性,实际上也存放了每个文件的内容的块在 data block中的起始位置。inode前面也说了是一个结构体,他的内部存在_le32 i_blockEXT2_N_BLOCKS;/* Pointers to blocks */ , EXT2_N_BLOCKS =15,就是用来进行inode和block映射的。下面是inode的映射关系图:

有点文件的内容很多,势必要占用非常多的块,这个时候一个指针指向一个块就有些不够用了,就需要二级间接指针和三级间接指针,二级间接指针是指针指向一个表,表里面每一单位再指向块,三级间接指针则是中间又多加了一个表,这样下来就可以做到多个块的映射了。

3.补充

我们前面说了每个文件都有对应的inode编号,需要注意,inode编号和数据块,可以跨同一分区的不同组之间编号,但是不能跨分区,也就是说,不同分区之间的inode编号可能相同,同一分区的inode号则唯一。

我们可以看看touch一个文件时系统会大概怎么做:

创建一个新文件主要有以下4个操作:

  1. 存储属性:内核先找到一个空闲的 i 节点(这里是263466)。内核把文件信息记录到其中。

  2. 存储数据:该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推。

  3. 记录分配情况:文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。

  4. 添加文件名到目录:新的文件名abc。linux如何在当前的目录中记录这个文件?内核将⼊口(263466,abc)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来。

4.目录和文件名

在磁盘中,并不区分一般文件与目录文件,所有文件均是内容 + 属性。

我们前面说过,文件名并不作为文件属性存放在 inode table中,并且我们也说过,Linux下一切皆文件,所以,目录其实也是文件。文件名是作为目录文件的文件内容存储于当前文件所在的目录文件中,准确来说存储的是文件名与inode编号的映射关系。

我们在使用文件的时候都是通过文件名而不是inode值,原因就在这里。事实上,当访问一个文件时,会通过其所在目录文件查找到文件名对应的inode,然后操作系统再通过该inode进行相关操作,于此同时,又出现了新的问题:"目录文件也是文件,要如何找到该目录文件中的文件内容呢?",答案是需要从这个目录文件所在的目录的目录文件内容中查找......这么一直往前找,直到遇到根目录,而根目录的内容与属性在操作系统启动时就自动被加载到内存了,其中的文件内容系统本来就知道,然后沿着刚刚的一点点向下走,就可以找到所求的文件内容了,这个过程被称为"路径解析"。所以,实际上系统+用户共同构建Linux路径结构。

经过日常使用我们可以发现,文件一层一层往下递减直到目标文件,这样实际上就像是一颗树一样。

Linux中,在内核中维护树状路径结构的内核结构体叫做: struct dentry。每个文件其实都要有对应的dentry结构,包括普通文件。这样所有被打开的文件,就可以在内存中形成整个树形结构 。整个树形节点也同时会隶属于LRU(Least Recently Used,最近最少使用)结构中,进行节点淘汰。整个树形节点也同时会隶属于Hash,方便快速查找。更重要的是,这个树形结构,整体构成了Linux的路径缓存结构,打开访问任何文件,都在先在这棵树下根据路径进行查找,找到就返回属性inode和内容,没找到就从磁盘加载路径,添加dentry 结构,缓存新路径。

5.挂载分区

我们已经能够根据inode号在指定分区找文件了,也已经能根据目录文件内容,找指定的inode了,在指定的分区内,我们可以为所欲为了。可问题:inode不是不能跨分区吗?Linux不是可以有多个分区吗?我怎么知道我在哪一个分区???这实际上就要看路径了,我们前面说了windows的分区可以看成C/D盘,根据路径我们就可以判断你在哪个分区。

挂载(mount)的本质:把一整个独立磁盘分区,挂载到目录树上的某一个目录(挂载点)。原始根文件系统:/ 本身就是第一个挂载点,对应根分区。/home、/boot、/data 这类目录默认只是根分区内部普通目录只有安装系统时单独划分分区,再将分区 mount 到 /home,此时 /home 才属于独立分区。我们已知一个文件的inode,想要知道在哪个分区,是看这个文件路径中,挂载点是哪个分区,即拿到文件对应的 inode 之后,可以通过它所在路径回溯到挂载点,从而确定所属分区。

上面这段话可能读者看的不太懂,想了解更详细可以自行查资料,实际上想要简单理解的话知道我们可以通过文件路径确定该文件所在的分区即可。

4.软硬链接

前面在文件信息说了硬链接数,这里作者只对软硬链接做简单介绍:

1.硬链接

我们知道,真正找到磁盘上文件的并不是文件名,而是inode。其实在linux中可以让多个文件名对应于同一个inode。:

复制代码
[root@localhost linux]# touch abc
[root@localhost linux]# ln abc def
[root@localhost linux]# ls -li abc def
263466 abc
263466 def

abc和def的链接状态完全相同,他们被称为指向文件的硬链接。内核记录了这个连接数,inode 263466的硬连接数为2。我们在删除文件时干了两件事情:1.在目录中将对应的记录删除,2.将硬连接数-1,如果为0,则将对应的磁盘释放(实际上就是引用计数)。

我们可以把硬链接简单理解为文件备份。

2.软链接

硬链接是通过inode引用另外一个文件,软链接是通过名字引用另外一个文件,但实际上,新的文件和被引用的文件的inode不同,应用常见上可以想象成一个快捷方式。在shell中的做法:

复制代码
[root@localhost linux]# ln -s abc.s abc
[root@localhost linux]# ls -li
263563 -rw-r--r--. 2 root root 0 9⽉ 15 17:45 abc
261678 lrwxrwxrwx. 1 root root 3 9⽉ 15 17:53 abc.s -> abc
263563 -rw-r--r--. 2 root root 0 9⽉ 15 17:45 def

3.两者对比和补充

软连接是独立文件,硬链接只是文件名和目标文件inode的映射关系。

硬链接用途:每个目录下都会有.和..两个文件,它们就是硬链接,除此之外还可以文件备份。

软链接用途:类似快捷方式。

相关推荐
GeW19 分钟前
当Linux内核开始“确定性”,制造高端化才有了底层语法
linux
星恒随风2 小时前
MySQL 从零入门(三):表的约束详解
linux·服务器·数据库·笔记·mysql
StarsGreen4 小时前
libva在intel平台上的前端流程简介
linux
坤坤子吖7 小时前
Linux网络编程:自定义应用层协议与 JSON 序列化
linux·服务器·网络·c++·笔记·json
Felven8 小时前
华为鲲鹏920s芯片serdestool工具使用说明
linux·运维·服务器·华为·鲲鹏920·serdestool
莫晓燚9 小时前
mall 电商项目完整部署---实战
linux·运维·docker·容器
咋的啥名都用不了呢~9 小时前
Linux项目部署5(安装Nginx的插件)
linux
CodeYp20199 小时前
第11讲:应用层协议 HTTP
linux·http
爱和冰阔落9 小时前
【Linux网络】JsonCpp 怎么实现序列化?从 Request/Response 到网络计算器消息设计
linux·运维·网络·php