磁盘与内存 Linux文件系统
相关的概念和区块划分
磁盘
磁盘的组成
磁头(head):不用说,主要就是读取磁盘表面磁方向和改变其方向,每个盘面有一个磁头,它极其贴近地悬浮在盘面上,但是绝对不与盘面接触,否则会损坏磁头和盘面;
磁道(track):磁道是单个盘面上的同心圆,当磁盘旋转时,磁头若保持在一个位置上,则每个磁头都会在磁盘表面划出一个圆形轨迹,这些圆形轨迹就叫做磁道,一个盘面上的磁道可以有成千上万个。相邻磁道之间并不是紧挨着的,这是因为磁化单元相隔太近时磁性会产生相互影响,同时也为磁头的读写带来困难。
柱面(cylinder):在有多个盘片构成的盘组中,由不同盘片的面,但处于同一半径圆的多个磁道组成的一个圆柱面。
扇区(sector):磁盘上的每个磁道被等分为若干个弧段,这些弧段便是硬盘的扇区(Sector)。硬盘的第一个扇区,叫做引导扇区。扇区是被间隙(gap)分割的圆的片段,间隙未被磁化成0或者1。注意,扇区是读写磁盘最基本的单位,如果一个扇区因为某种原因被破坏,那么整个扇区的数据都会受影响。
Linux系统和磁盘
Linux文件系统中,文件和属性 是分开存储的。
磁盘被访问的基本单位时扇区,大小可能是512个字节等(以下统一将扇区当作
512字节
处理)。我们可以把磁盘看成是无数个扇区构成的存储介质,我们可以把数据存到磁盘,第一个解决的问题是定位扇区。
如何定位?
- 哪一个柱面(Cylinder)
- 哪一个磁道(Heade)
- 哪一个扇区(Sector)
- 即CHS寻址方式
如何判断磁盘的效率高低呢?
- 运动越少,效率越高
- 运动越多。效率越低
逻辑关系(LBA地址法)
- 磁盘文件在逻辑上,我们将它们看成是线性的连续的。
假设现有一个盘面有20000
个扇区,每个盘面有50
个磁道,每个磁道有400
个扇区。现有一块数据的编号是28888
那么,我们可以得知:
- 28888/20000 = 1(第一盘面)
- 28888%20000 = 8888 –> 8888/400 = 22(第22号磁道)
- 8888 % 400 = 88(第88号扇区)
以上方法即可以访问磁盘文件的方法。
外设寄存器
不仅仅是CPU具有寄存器,其他的外设也有。例如磁盘中:
- 控制寄存器:控制IO方向(r/w)。
- 数据寄存器:存放数据。
- 地址寄存器:地址寄存器(填写LBA)。
- 状态寄存器:检查是否操作成功。
管理分区
假如我们有一个800G的磁盘,系统在管理的时候,会对它进行分区。
其中记录分区的一种方法和我们在
进程地址空间
内描述的类似。使用一个有关分区的结构体数组,每一个结构体内包含这个分区的起始地址和结束地址。
inode
inode
(索引节点)是文件系统中的一种数据结构,用于存储文件的元数据。文件系统通过inode
来管理和访问文件。每个文件和目录在文件系统中都有一个唯一的inode
,这个inode
包含了文件的元数据信息,但不包含文件名和数据内容。
inode
包含的信息:
- 文件类型:如普通文件、目录、符号链接等。
- 文件权限:文件的读、写、执行权限。
- 文件所有者和组:文件所属的用户ID和组ID。
- 文件大小:文件的字节数。
- 时间戳:文件的创建时间、最后修改时间、最后访问时间等。
- 硬链接计数:指向该
inode
的硬链接数量。 - 数据块指针:指向存储文件数据的实际磁盘块的位置。
inode
不包含的信息:
- 文件名:文件名存储在目录结构中,而不是在
inode
中。目录将文件名与对应的inode
号关联起来。 - 文件数据内容:
inode
本身只包含指向文件数据所在位置的指针,而不直接存储文件数据。- 只有索引的
叶子节点
才存储文件数据。
- 只有索引的
作用:
- 当你访问一个文件时,系统首先通过文件名在目录中查找对应的
inode
号。 - 然后,通过
inode
号找到对应的inode
,从中获取文件的元数据以及文件数据所在的磁盘块位置。 - 最后,系统根据这些信息访问文件的数据内容。
inode
号:
每个inode
都有一个唯一的标识号,称为inode
号。文件系统通过inode
号来管理文件,允许多个文件名(通过硬链接)指向同一个inode
,实现不同路径指向同一个文件。
inode
的作用:
- 文件管理:
inode
是文件管理的核心,提供文件的元数据和数据存储位置。 - 硬链接支持:
inode
使得硬链接成为可能,即不同路径可以指向同一个文件数据。 - 文件系统性能:
inode
结构优化了文件的存取速度。
示例:
你可以使用 ls -i
命令来查看文件或目录的inode
号:
1 |
|
这里 123456
就是文件 file.txt
的inode
号。
分组分块
对于一个分区,会再将它划分为多个组,随后再划分为多个块。
Super Block
记录了文件系统的基本信息—— 存在多个Super Block
并且使用双链表维护。
例如:- 每个组的大小。
- 每个组的
inode
数量。 - 一共有多少个组。
- 每个组的起始inode。
- 文件系统的类型与名称等。
Super Block
会存在于若干个分区,作为备份以供发生意外情况时修复文件系统。但并不是每个分区都有,这样会造成浪费)。
Group Descripitor Table
描述的是整个分组的基本使用情况。例如:inode
的总数量。boot block
:包含一些操作系统启动的一些信息。Date blocks
:存取操作的区域,都是以块的字节大小来操作的,常见的是4KB大小。并且一个分区支持有多个Date blocks
。inode Table
:用来记录多个inode
——其中Date blocks
的属性。并且inode
有唯一的编号,并且是以分区为单位来进行划分的,不能跨分区。block Bitmap
:比特位和块号之间的映射,其映射关系是该块号是否有被使用过。inode Bitmap
:其映射关系是该inode编号是否有效。
删除(删除
等于允许被覆盖
)文件只需要将文件对应的位图(inode
和block
的Bitmap
)初始化即可——这也就是一般删除文件速度很快的原因。也是可以恢复数据的原因。
故Linux中,磁盘文件是属性和数据是分开存储的。
格式化
- 每一个分区在被使用之前,都必须提前先将部分文件系统的属性信息提前设置进对应的分区中,方便我们后续使用这个分区或者分组。
情况
新建一个文件的时候,发生了什么?
查看
Group Descriptor Table
,查看inode使用情况
,如有空余,则从inode Bitmap
中扫描到最近一个没有被使用的位置,就分配给新建的文件。然后变更inode Bitmap
(代表其已经被使用了)。当要进行写入操作时,查看
Group Descriptor Table
,查看data blocks使用情况
,如有空余,则从Block Bitmap
中,扫描到最近一个没有被使用的位置,就分配给新建的文件。然后**变更block Bitmap
**。并且将此信息告诉对应的inode
。
目录
目录也是文件,也有自己的inode
,也有自己的属性,并且目录也有自己的数据块。存放的是该目录下,文件名和(该文件)inode的映射关系。
因此,我们便可以解释以下问题:
为什么同一个目录不能有同名文件?:会导致映射关系错误。
对于一个目录,没有
w
权限,为什么我们无法创建文件?:无法将创建的文件信息写入到目录的数据块内。对于一个目录,没有
r
权限,为什么无法查看文件?:无法读取目录的数据块(无法查看映射关系)。对于一个目录,没有
x
权限,为什么无法进入目录?无法使用cd
命令,也就没办法访问目录。
寻找文件,也就是递归寻找inode
的过程,直到根目录
,根目录的信息是确定的。
Dentry缓存
由于递归到根目录会浪费较多的资源,于是操作系统会将我们常用的文件信息的inode
缓存起来。
物理内存和磁盘
操作系统也需要管理物理内存。当内存和磁盘进行交互的时候,文件系统会将内存划分为一个个页框,每个页框可以对应虚拟内存中的一个页。
页框是物理内存中的一个单位。操作系统将物理内存划分为与虚拟内存页大小相同的页框。
当内存不足时,操作系统会将某些不常用的页从内存中移出,并保存到磁盘上的交换分区或页面文件中。这种机制叫做“换页”。这些保存到磁盘上的页可以被称为“交换页”或“页面文件中的页”。
这样做的原因:
- 类似页帧的打包方式(比如一次打包
4kb
而不是一字节一字节读取
)可以减少硬盘CHS寻址次数。减少磁盘IO次数。 - 利用局部性原理,实现一定的程序预加载机制。
页和块的区别
页(Page):
- 内存管理单位:页是操作系统用来管理内存的基本单位。操作系统通过分页机制来管理虚拟内存和物理内存之间的映射关系。
- 虚拟内存:在虚拟内存系统中,内存被分成大小相等的页。虚拟内存中的每个页都可以映射到物理内存中的某个页框(Page Frame)。
- 大小:常见的页大小通常为4KB,但也有其他尺寸(如2MB或1GB),具体取决于系统架构和配置。
- 分页机制:分页允许操作系统将一个进程的内存分成小块,以提高内存利用率并支持多任务处理。
块(Block):
- 磁盘存储管理单位:块是文件系统用来管理和存储磁盘数据的基本单位。文件系统以块为单位来分配和管理磁盘上的数据。
- 文件系统:在文件系统中,文件数据被存储在若干个块中。每个文件至少占用一个块,哪怕文件大小小于块的大小。
- 大小:块的大小通常为4KB,但也可以是512字节、1KB、2KB、8KB等,具体取决于文件系统的类型和配置。
- 数据传输:块是磁盘与内存之间数据传输的基本单位。操作系统在读取或写入磁盘数据时,通常是以块为单位进行的。
具体如何管理内存
已知内存会被操作系统划分为一个一个页。
Linux内核中提供了一个
struct page
用来存放page
页的必要属性信息——例如这个页是否有被使用过,或者权限相关的信息。
并使用一个数组struct page_mem_array
将他们组织起来。
- 假设一个
页框
的大小是4kb
。 - 一个4G的物理内存就有(1024
*
1024*
1024*
4)/
(1024/
4)个页框。 struct page_mem_array[]
就要负责存储这些页框的物理地址。
对于一个物理地址,我们只需要除以4kb(2^12byte页大小)—— 即 &0xFFFF F000
,就可以得到它在page_mem_array[]中的下标,然后得到内存中该物理地址处(页)的信息。
假如一个程序编译好了,但是它没有运行,内部有地址的概念吗?
- 有,程序编译好之后,就会有地址的一些信息——即虚拟地址(逻辑地址)(排布方式:平坦模式)。
磁盘程序加载到内存
首先,为要执行的进程创建PCB,并且将进程的
入口函数
(也是虚拟地址)地址信息加载到CPU的对应寄存器。如(EIP/PC)。此时页表中还没有填写对应的内容,所以会触发缺页中断。
中断之后,操作系统会将我们程序的内容加载进内存中。
一旦程序被加载进来之后,就具有了物理地址,然后将虚拟地址和物理地址通过页表映射。