1.认识磁盘结构
机械键盘是计算机中唯一的机械设备,磁盘是外设,容量大,速度慢,价格便宜
物理结构:
- 磁头是一面一个,左右摆动,两个整体移动的,有磁头停靠点
- 磁头和盘面不接触,所以物理上不适用于笔记本,开机状态移来移去,磁头刮花了磁盘,属于硬件问题
- 机械磁盘要在无尘环境下,灰尘落上去可能会把数据都磨没了
- 所有的数据都在盘片上以二进制存储,磁头通过充放电写入
- 内存掉电易失设备,磁盘永久性存储介质
- 通过充放电/强弱/波,在磁盘上写入 01 数据,像吸铁石的 N S 级
存储结构:
磁盘被访问的最小基本单元是扇区--大小约为512字节,块设备
如何定位一个扇区呢?
CHS寻址:先定位磁头(header),确定磁头要访问哪一个柱面(磁道)(cylinder),定位一个扇区(sector)
逻辑结构:
我们可以把磁带拉直,形成线性结构
那么磁盘本质上虽然是硬质的,但是逻辑上我们可以把磁盘想象成为卷在⼀起的磁带,那么磁盘的逻辑存储结构我们也可以类似于
这样每⼀个扇区,就有了⼀个线性地址(其实就是数组下标)
这种地址叫做LBA(Logical Block Address)
假设是两片四面。尽管扇区大小有差异,但是每个扇区的内存大小都是一样的。每一面上都有很多扇区,最后就把磁盘抽象成数组 。
有了数组,就可以通过下标找到某个扇区,但是磁盘只认CHS,所以要通过算法将下标转换成CHS的地址。
找到下标后交给磁盘,磁盘内部会把线性地址转换成CHS的地址,进而定位到某一个扇区里。
注意:一般而言,OS未来和磁盘交互的时候,基本单位为4KB,而不是512字节(一个扇区512字节),因为一次读512字节太少了,因为要提高效率
所以4KB=8个连续的扇区,系统把这8个连续的扇区称为块大小(数据块)
8个扇区为一个块,块号*8 = 每一个块的第一个扇区的下标,连续往后读就能知道整个块的下标
对于OS而言:未来读取数据就能以块为单位了,这里的每一个块号的起始地址称作LBA (逻辑区块地址)
LBA和CHS转化(了解)
从此往后,在磁盘使⽤者看来,根本就不关⼼CHS地址,⽽是直接使⽤LBA地址,磁盘内部自己
转换。所以:从现在开始,磁盘就是⼀个?元素为扇区?的⼀维数组,数组的下标就是每⼀个扇区的LBA地址。OS使用磁盘,就可以⽤⼀个数字访问磁盘扇区了。
2.文件系统
引入块的概念:
其实硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,其实是不会⼀个个扇区地读取,这样
效率太低,⽽是⼀次性连续读取多个扇区,即⼀次性读取⼀个”块”(block)。
硬盘的每个分区是被划分为⼀个个的”块”。⼀个”块”的⼤⼩是由格式化的时候确定的,并且不可
以更改,最常⻅的是4KB,即连续⼋个扇区组成⼀个”块”。”块”是⽂件存取的最⼩单位。
理解分区
其实磁盘是可以被分成多个分区(partition)的,以Windows观点来看,你可能会有⼀块磁盘并且将它分区成C,D,E盘。那个C,D,E就是分区。分区从实质上说就是对硬盘的⼀种格式化。但是Linux的设备都是以⽂件形式存在,那是怎么分区的呢?
假设磁盘的空间很大,需要分区管理,因此只要能管理好其中一个区,剩下的其他区就可以照搬它的管理方法,全部管理好。(分治思想)
如果这里管理其中一个区,这个区还是太大,就要在这个区里面继续分组。只要把一个组管理好了,这个区的每一个组就都能管理好了
磁盘文件系统图 :
Linux文件系统特点:文件内容和文件属性 分开存储
Block Group:文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成
超级块(Super Block):存放文件系统本身的结构信息,描述的是整个分区整体的文件系统的情况
1.记录的信息主要有:
1.bolck 和 inode的总量
2.未使用的block和inode的数量
3.一个block和inode的大小
4.近一次挂载的时间
5.最近一次写入数据的时间
6.最近一次检验磁盘的时间等其他文件系统的相关信息
2.Super Block 的信息被破坏,可以说整个文件系统结构就被破坏了
3.超级块不止一个,可能有多个,在不同的块组里,这样可以让文件系统更稳定
GDT,Group Descriptor Table:块组描述符,描述块组属性信息
块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
inode表:存放文件属性 如 文件大小,所有者,最近修改时间等
数据区:存放文件内容
inode Table里面存的是文件的属性,Data blocks里面存的是文件的内容
Linux中文件的属性是一个大小固定的集合体,也就是inode结构体结构体里面没有文件名。内核层面,每一个inode都有inode number,通过inode号标识一个文件。通过inode号找到inode后,inode里面还有一个数组,可以映射到对应数据块
理解inode
在linux文件系统中, inode(索引节点)是一个非常重要的概念。每个文件和目录在Linux文件系统中都有一个对应的 inode ,它包含了除文件名之外的所有元信息(文件属性)
文件内容与inode
映射
inode
结构中除了有编号属性外,还有一个属性,这个属性是存储指向data Blocks
中指定数据块的指针,其有四种分类:
1.12个直接映射指针,每个指针直接指向每一个存储内容的数据块,一共可以存储4kb * 12 = 48kb的数据,对于小文件非常有效
2.一级指针,指向一个数据块,一个4kb的数据块中每4个字节存储其他块的索引,此时有4 * 1024 / 4 * 4 * 1024 = 4MB个存储空间
3.二级指针,指向一个数据块,一个4kb的数据块中每4个字节存储与一级指针指向的数据块相同的数据块,此时有4 * 1024 / 4 * 4 = 4096MB = 4GB个存储空间
4.三级指针,指向一个数据块,一个4kb的数据块中每4个字节存储与二级指针指向的数据块相同的数据块,此时有4 * 1024 / 4 * 4 = 4096GB = 4TB个存储空间
inode的定义
inode 是文件系统中的一个数据结构,用于存储文件的元数据(即文件的属性信息)
每个文件或目录都有一个唯一的 inode 号,注意:一个文件inode是有可能有多个目录项的,比如给一个文件创建多个硬链接,因此文件 和 inode 不是 一一对应
inode 编号是以分区为单位的,一个分区内 inode 号不能重复,两个分区间可以,所以 inode 不能跨分区访问。
inode包含的信息
文件类型:普通文件、目录、符号链接等
权限:文件的读、写、执行权限
所有者:文件的所有者和组
时间戳:文件的创建时间、修改时间和访问时间
文件大小:文件的实际大小
指向数据块的指针:文件数据在磁盘上的位置
硬链接数:指向该inode的目录项数量
inode的作用
唯一标识: inode 号是文件系统的唯一标识符,即使文件名改变, inode 号也不会改变。
元数据管理: inode 存储了文件的所有重要元数据,使得文件系统可以高效地管理和检索文件。
硬链接支持:多个目录项可以指向同一个 inode ,这就是硬链接的基础。
inode与文件名的关系
文件名实际上是目录中的一个条目,指向相应的 inode可以有多个文件名(硬链接)指向同一个 inode
删除一个文件名并不会删除 inode ,只有当所有的硬链接都被删除时,inode 才会被释放
查看inode信息
可以使用一些命令来查看inode信息:
ls -i:显示文件和目录的 inode 号。
stat :显示文件的详细信息,包括inode号和其他元数据。
df -i:显示文件系统的 inode 使用情况。
inode限制
每个文件系统有一个固定的 inode 数量上限,这在创建文件系统时确定
如果文件系统中的 inode 用尽,即使还有可用的磁盘空间,也无法再创建新的文件或目录
Inode 、Inode Table 、Data Blocks 三者关系
Inode 是一个数据结构,用于存储文件的元数据(即文件的属性信息)。每个文件和目录都有一个唯一的 inode
Inode Table 是文件系统中存储所有inode 的一个区域。它是一个固定大小的数组,每个 inode 占用固定的大小。作用如下:
Inode Table 存储了文件系统中所有文件和目录的元数据
每个 inode 在 Inode Table 中有一个唯一的编号(inode号)
当文件系统创建时,Inode Table 的大小是预先分配好的,这意味着文件系统中可以存储的文件和目录数量有一个上限
Data Blocks 是实际存储文件内容的磁盘空间。文件的数据被分割成多个块,并存储在这些数据块中。作用如下:
文件的实际内容存储在Data Blocks中
Inode中的指针指向这些数据块的位置
数据块的大小通常是固定的,例如4KB或8KB,具体取决于文件系统的配置
三者的关系
Inode 存储文件的元数据,并包含指向数据块的指针。
Inode Table 是存储所有inode 的区域,每个inode 在其中有一个唯一的编号。
Data Blocks 存储文件的实际内容,通过inode 中的指针来访问。
格式化
操作系统在建立完分区之后就需要对当前分区进行分组,而这个过程也被称为格式化分区,本质上就是向Group Descriptor Table和Super Block中写入数据,既然要写入数据,就需要先获得到对应的数据,操作系统会根据一定的比率分配inode Table和data blocks,所以整个分区的大小都是固定的,这就会导致出现inode用完,但是data Blocks没有用完。所以前面说「可以确定起始值是因为组内每一个字段以及大小都是固定的」
需要注意,同时也存在出现inode没用完,但是data Blocks用完的情况
实际上,如果一个分区不进行格式化,那么这个分区就无法被用户使用,除了因为制定的分组信息没有填写外,还存在没有给当前分区建立目录索引的原因。这个原因在接下来的内容中会提及
如何查找文件
前面提到每一个文件有对应的属性,而属性存储在inode结构中,操作系统为了查找一个文件首先就需要对应文件的inode结构的编号成员,接着根据这个编号减去当前分组的inode Table的起始值就可以算出一个偏移量,在inode Bitmap中根据这个偏移量判断指定inode编号是否有效,即判断这个编号在inode中的对应位置是否为1,如果为1,证明指定文件存在。
因为每一个分组的起始值不同,导致inode Table中inode编号的偏移量加上对应的起始值就不会相同,所以就可以解释为什么inode不会相同
接着在该inode结构中根据数据区指针指向的数据块在block Bitmap中判断是否有效,如果有效,就找出对应的文件内容即可
如果现在这个文件非常大,其内容不仅在当前组中存在,也存在于其他组,此时就需要不同组之间的Data Blocks进行连接,所以实际上,Data Blocks在Linux中是可以跨分组的,但是不可以跨分区,因为不同的分区可能使用的文件系统不同
对于其他的「如何删除文件」、「如何修改文件」以及「如何新增文件」都涉及到「如何查找文件」,所以基本思路都是一致的
对于删除文件来说,本质上就是先进行查找文件的操作,而删除的过程就是将对应的inode Bitmap和block Bitmap对应位置置为0,此时就表示指定的文件内容和属性无效,新的文件或者已有的文件可以使用对应的数据块
正是因为有上面的删除机制,所以删除的文件理论上是可以被恢复的,但是恢复的难度很大
对于修改文件,依旧是先查找文件,再修改文件对应的数据块的内容即可
对于新增文件,之所以要先查找,是因为需要防止出现文件冲突,如果没有发生冲突,就需要先在inode Bitmap中申请一块空间(将指定位置记为1),在根据这个位置加上指定inode Table的起始值就可以找到一个位置存储inode节点,再将inode节点中的编号属性赋值即可
文件与目录
前面提到「如何查找一个文件」是通过获取到的inode编号,但是现在的问题是,系统如何拿到的inode编号。换句话说,在使用Linux命令行操作时,用户输入的文件名或者目录名都是字符串,系统是如何知道指定文件对应的inode编号的?
在inode结构中不存储着文件名,但是系统能根据文件名获取到其对应的inode编号,主要原因是文件所在路径,为了更好理解下面的内容,先讨论目录文件:
对于底层磁盘硬件来说,不论是目录还是普通的文件,它们都是由二进制组成,而系统要找到某一个目录就需要根据当前目录的所在路径从根路径开始解析(根路径是系统加载时一定会被固定加载的),直到进入到当前目录的数据块,而在目录中的数据块存储着当前目录中所有文件名字符串和其inode编号的映射关系
一般来说,这个映射关系是相互映射的
所以系统获取一个文件的inode编号只需要根据其所在路径按照路径解析进入到其所在目录找到映射关系就可以获取到
但是,如果同一个目录有很多个文件,对每一个文件都进行路径解析,那么系统整体的效率就会受到影响,为了尽可能提升系统效率,在Linux中还存在一个多叉树结构dentry,这个结构的根节点就是根路径,剩下的节点就是按照文件路径中的目录构成的一个一个节点。与进程PCB结构一样,dentry结构也是内存级的结构
有了上面的概念,所以在一个程序中之所以可以使用open打开一个文件本质就是因为进程的CWD给出了当前程序的路径,即根据这个路径找到该进程要打开的文件
至此,系统获取到文件的inode编号的方式就已经基本确定,现在也就可以基本解释在前面权限部分提到rwx三个权限为什么缺失时目录会用相应的效果:
缺失r权限,无法读取目录中的内容:本质就是无法获取到目录中的文件名和inode编号的映射关系
缺失w权限,无法修改目录中的文件:本质就是无法向目录的数据块写入
缺失x权限,无法进入目录:本质就是无法进入到目录的数据块中