前言:今天我们来探讨在磁盘上的文件。然后我们将学习 Inode,带着大家理解文件系统,为下一章介绍软硬链接做铺垫。
磁盘级别的文件管理,本质工作和快递驿站的老板做的工作是一样的!
对磁盘这个大空间的合理划分,让我们能快速定位查找到指定文件,乃至进行相关后续访问操作。
这就是所谓的 文件系统 (File-Sys)
这些文件是又多又杂又乱!
小至几 kb,大至几 GB 的都有,文件类型也是各种各样 .jpg, .sh 什么都有,文件所属组,
文件类型,只读或者只写,各种各样的 "可能被打开的" 文件都有!所以我们是需要管理它们的。
如果要理解文件系统,我们不妨把视角从内存中移开 (虽然和内存有关系) ,重点关注磁盘。
磁盘上存储的基本单位是 扇区 (Sector),一般是 512 字节的。近三十年来,扇区的大小一直是 512 字节,但最近几年正迁移到更大、更高效的 4096 字节扇区,通常称为 4K 扇区。
读写磁盘的时侯,磁头找的是某一个面的某一个 磁道 (Traker),的某一个扇区 。
文件系统:什么文件,对应了几个磁盘块。
只要我们能找到磁盘上的盘面,柱面 (磁道) 和扇区,我们就能找到一个存储单元了。
用同样的方法,我们可以找到所有的基本单元。
所以,我们这种在物理上查找某一个扇区的寻址方式,叫做 地址。
磁盘存储数据,磁性 N/S,改变 NS 极,就是改变了 0 1。
结论:你的文件数据就在这个盘面上。
我们可以把对应的盘片,想象成为线性的结构。
定位一个 sector,只要找到下标就行了!对于磁盘的管理,转化成为了对数组空间的管理。类似于数组。磁盘上的每个扇区可以被看作是数组中的一个元素,而扇区的编号可以被看作是数组的索引。在这种情况下,可以使用索引来定位一个特定的扇区,就像在数组中使用索引来访问数组元素一样。磁盘管理的任务就变成了对这个线性结构(即盘片)的管理,包括读取和写入特定扇区的数据。
这个抽象过程,仍然是 "先描述在组织"。
而这个下标,就是 LBA (logic block arrays), 这是操作系统认为磁盘基本单元的地址,它是一种逻辑块地址。所以,未来你想在磁盘中写入:只需要将 LBA 地址映射转化成 CHS 地址,然后将该内存中的数据配合 CHS 地址写入到磁盘里,至此就完成了写入:
以前硬盘容量比较小,人们采用软盘的设计结构来设计生产硬盘,
硬盘盘片的每一条磁道都具备相同的扇区数量,由此就产生了 CSH 3D 参数 (Disk Geomentry)
即 磁头数 (Heads),柱面数 (Cylinders) 和 扇区数 (Sectors) ,以及对应的 CHS 寻址模式。
CHS 寻址模式是将硬盘划分为三个部分:磁头 (Heads)、柱面 (Cylinder)、扇区 (Sector) 。
CHS 寻址的最大容量由 CHS 三个参数所决定:
INT13:BIOS自带的第13号中断,mov ah,0/int13h复位磁盘;mov ah,2/int 13h读磁盘;mov ah,3/13h写磁盘。DOS 中的最有用,最危险的命令,可直接修改硬盘分区表。
由于 INT13 的限制,三维地址 CHS 的最大值只能为 ,
其次,在系统管理文件时记录繁琐的 CHS 是件很费力的事情,效果较低。
然而使用 逻辑扇区 (LBA) 后,可在磁盘读写操作时可以摆脱柱面、磁头等硬件参数的限制。
逻辑扇区,是为了方便操作系统读取写入硬盘数据而设置的,其大小与具体地址,都可以通过一定的公式与物理地址对应。操作系统可以根据 LBA 来读取和写入数据,而无需关心物理地址的具体映射方式。
在 LBA 模式下,操作系统可以把所有的物理扇区都按照某种方式或规则看作是一个线性编号的扇区,从 0 到某个最大值方式排列,并连成一条线。把 LBA 作为一个整体来看待,而不是具体到实际的 CHS 值,这样就只需要用一个序数就能确定唯一的物理扇区,这就是线性地址的由来。显然,线性地址是物理扇区的逻辑地址。
首先,我们需要知道 的基本单位 。
磁盘的基本单位是 扇区 (常规为 512 字节),文件系统访问磁盘的基本单位是 :
① 提高 效率
② 不要让软件 (OS) 设计和硬件 (磁盘) 具有强相关性,即 解耦合。
整体 IO 效率提高,将磁盘的数据拷贝到内存花费的时间并不多,花费多的是在磁盘内部寻找位置的过程,该过程是 寻址过程。不管磁盘是多少转,你是永远无法比得上光电信号的。
对如何管理文件,变成了对一个小组数据的管理。那么如何对一个组做管理?
我们使用 ls -l
时,除了能看到文件名,还能看到文件的 元数据 (Metadata) :
每行包含七列,分别是: 模式、软硬连接数、文件所有者、组、大小、最后修改时间和文件名。
ls -l
做的就是读取存储在磁盘上的文件信息,然后把它们显示出来:
这个元数据除了通过这个方式来读取,还有可以通过 stat 命令看到更多的信息。
在讲解上面这些信息前,我们需要了解 inode 的概念。
ext2 文件系统,下图为磁盘文件系统图(当然了,内核中内存映像肯定有所不同):
磁盘是典型的块设备,磁盘分区被划分为一个个小的 block。
一个 block 的大小是由格式化时决定的,并且不可修改。
例如 mke2fs 的 -b 选项可以设定 block 大小为 1024, 2048 或 4096 字节。
注:mke2fs 是一个语法,用于建立 ext2 文件系统。(make ext2 file system)
mke2fs [-cFMqrSvV][-b <区块大小>][-f <不连续区段大小>][-i <字节>][-N ][-l <文件>][-L <标签>][-m <百分比值>][-R=<区块数>][ 设备名称][区块数]
上图中,启动块 (Boot Block) 的大小是确定的。
每个 Block Group 都有着相同的结构组成。
记录的信息主要有:block 和 inode 的总量,未使用的 block 和 inode 的数量,一个 block 和 inode 的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。
如果 Super Block 的信息被破坏,可以说整个文件系统结构就被破坏了。
计算机在启动时,BIOS(基本输入/输出系统)会首先运行。
BIOS 是计算机主板上的固件程序,用于管理计算机的基本硬件和操作。BIOS包括一系列的自检程序(POST),用于检查计算机硬件是否正常工作。如果检查顺利通过,BIOS会将控制权交给操作系统,让操作系统接管计算机的控制权,进而启动操作系统并加载应用程序。如果检查失败,BIOS会发出声音或闪烁指示灯等提示,以表明出现问题。
Data Blocks:以块为单位,进行文件内容的保存。
Inode Table:以 128 字节为单位,进行 inode 属性的保存。
文件 = 内容 + 属性,所以都是数据,都要数据,Linux 采用的是将内容和属性分开存储的方案。
struct inode {
int id;
mode_t mod;
user name;
data d;
...
}
struct inode i = {};
查看文件的inode:
块位图 Block Bitmap 中 记录着 Data Block 中哪个数据块已经被占用,哪个数据块没有被占用。
因此,我们可以利用比特位的内容来表示是否被占用:
inode 位图 (inode Bitmap) :表示 inode 块是否被占用,inode bitmap 表征 inode 的使用情况。
块组描述符 GDT (Group Descripter Table):描述块组属性信息,有多少 inode,起始的 inode 编号,有多少 inode 被使用,有多少 block 被使用,还剩多少,你的总 group 大小是多少……
那么,一个 inode (文件, 属性) 如何和属于自己的内容关联起来呢?
在 inode table 内包括了文件的所有属性,其中有一个 blocks 数组,直接保存了该文件对应的 blocks 编号,我们 通过 blocks 编号就可以找到自己文件的内容。
struct inode 包括文件所有的属性:
struct inode {
// 文件的所有的属性
block[15];
};
block[15] 中,[0,11] 中直接保存的就是该文件对应的 blocks 编号。
直接指向对应的 data blocks 中的某些块,这样就可以找到文件对应的某些内容了。[12,15] 指向一个 datablock,但是这个 datablock 不保存有效数据,而保存该文件所使用的其他块的编号!
" 一个 inode 是可以和多个块建立关联的 "
注意:文件名也算文件的属性,但是 inode 里面并不保存文件名!
Linux 下,底层实际都是通过 inode 编号标识文件的。
要找到文件一定要找到 inode 的编号,就知道找到分区内的哪一个小组,这个组的编号是一个到两万的,可以通过inode编号确定你在哪一个组。
下面我们就要重点谈一谈 Linux 下一切皆文件,目录是文件吗?当然也是文件!
如果目录是文件,那么根据 "文件 = 内容 + 属性" ,目录既然也是文件,那么它的属性也必须得有自己的 inode。一个目录也有自己的 inode 属性,我们目录里面内容对应的又是什么呢?
有文件也有对应的 blocks,目录包括文件类型,文件的所属组,文件的创建大小,
关键是一个目录如果有了inode 这个目录的数据块要放什么呢?
目录文件也有自己的 inode 也有自己的数据块,它的数据块里放的是 文件名 和 inode 的映射关系!
对我们来说,目录也是文件,也有自己的属性,目录文件里保存的是目录文件和 inode 编号。
文件名:inode 编号的映射关系。文件名和 inode 编号是数据,最终保存在了目录内容中。
当我们创建一个文件,操作系统做了什么?
我们创建一个文件的时候,一定是在一个目录下的。文件名 inode 编号 -> 找到自己所处的目录 data block -> 将文件名和 inode 编号的映射关系写入到对应的目录的数据块中。
这也就解释了为什么 inode 里为什么不保存文件名了,因为文件名在目录中。
知道自己所处的目录名,是否就能知道该目录的 inode?
如果我们想要知道目录的 inode ,我们需要要到父目录去查找对应关系。因此,知道自己所处的目录下,是不能知道目录的 inode 的。
当我们删除一个文件,操作系统做了什么呢?只需将标记 inode bitmap 由 1 置为 0 即可:
所以,这实际上是一个 伪删除!
如果我们把文件删了,我们可以恢复这个文件,如果要 恢复文件只需要搞到曾经删除的 inode 值就行了。通过一些工具,将 bitmap 从 0 恢复成 1 就可以了。
当我们执行删除文件操作时,操作系统实际上会在文件系统的目录结构中删除该文件的目录项,并将该文件的 inode 节点中的链接数减 1。如果链接数变为 0,则该文件的数据块将被释放,并将 inode 节点标记为可用状态。
然而,删除文件并不意味着文件的数据就被立即清除,因为该文件可能被其他进程或操作系统本身仍然使用或打开。因此,只有当该文件的所有链接数都为 0 时,文件的数据才会被完全清除。
此外,在某些情况下,操作系统也可能使用一些特殊的工具来覆盖文件的数据,以确保文件内容不可恢复。这种覆盖方式被称为 安全删除 或 彻底删除。
恢复的最大难点:文件都删掉了,你怎么知道 inode 是多少呢?Linux 系统为了支持恢复,inode 编号会保存在系统的日志文件中的。恢复有点难度!
实际上 Windows 也是这样的,几乎所有的文件系统删文件都不会真的删文件。
创建一个新文件,操作系统主要会做如下四个操作:
感谢阅读!!!!!!!!!