【Linux】基础IO------理解文件系统(inode)
目录
一、磁盘:
1、认识磁盘:
2、磁盘的存储构成:
3、磁盘的逻辑结构:
4、CHS寻址方式:
二、inode:
1、磁盘分治:
2、Group:
Data Blocks:
inode Table:
inode bitmap与block bitmap:
group descriptor table:
Super block:
三、文件相关操作:
前言:
前面我们一直学的是已经被打开的文件(内存文件),如果我们想对未被打开的文件(磁盘文件)进行访问,那么需要怎么访问呢,并且未被打开的文件是在哪儿的呢? ---------- 是在磁盘中的,所以也叫做磁盘文件,磁盘文件存在于专门的存储设备中的,因此磁盘文件存在的意义是将文件更好的存储起来,以便后续对文件进行访问
一、磁盘:
1、认识磁盘:
那么我们先来了解什么是磁盘呢?
在我们小的时候都用过那种光盘放在机电盒里面看动画片,那种光盘一面是光滑的另一面是用一种技术写上这个光盘里的主题,
我们现在学的这个磁盘和光盘是不一样的,磁盘两面都是光滑的,
磁盘主要分为机械硬盘和固态硬盘,前者读取速度慢,但便宜、稳定;后者读取速度快,但价格高昂且数据易损,两者各有其应用场景,本文主要介绍的是机械硬盘
组成:
机械硬盘是电脑中的唯一一个机械设备(以前的电脑或者现在的部分台式机,如今笔记本用的都不是这种磁盘,用的是固态硬盘SSD)
机械硬盘的结构:
磁盘:一片有两个面,每一面都可以存储数据,一层一层的盘片组成一个柱面
磁头:磁头是一面一个,磁头和盘面是不接触的,它是定位磁道和柱面的过程,它会一起左右摆动
主轴:主轴下面有马达,用于控制整块盘的转动
还有很多......注意:多个盘片、多个磁头类似一体的,是一起转的
因为这是个机械设备,所以运行起来是相对于CPU是很慢的,
数据存储:
在计算机中,数据是以二进制的形式存储的,也就是说只有0和1,对应着物理方面就是强信号代表1弱信号代表0,或者南极与北极,或者波峰与波谷,磁盘是以南极和北极来进行0,1的存储的
当我们进行数据存储的时候,数据本质上存储在磁盘的表面,虽然看起来是光滑的,但是实际上是凹凸不平的,当我们存数据的时候,磁头会向磁盘的盘片进行充放电,这个时候就会改变当前永磁体的南北极,进而进行数据的读写,
磁头和盘面之间不是挨在一起的,中间有空隙,当对机械硬盘进行搬动的时候,这个磁头会上下摆动,与盘面可能会发生摩擦,这个时候数据就可能会丢失,所以笔记本用的就肯定不是这种,而是固态硬盘
2、磁盘的存储构成:
我们由大到小地看:
上面绿色的就是一个磁头(head)
白色圆盘就是一个个的盘面
一个个盘面组成了柱面(cylinder)
盘面上面有一个个同心圆,这一个个同心圆就叫做磁道,每一个磁道又被分为一个个小块,这一个个很像扇形,就叫做扇区(如上图的蓝色区域,英文名叫做sector)
我们可以把磁盘看做由无数个扇区构成的存储介质
扇区是能够被访问的最小最基本的一个单元(大小有,512字节/4KB)
我们可以看到,在靠外面的扇区是大点的,靠圆心的扇区是更小的,那么它们存储的大小为什么都是一样的呢?------- 只需靠外面的0 1之间间距大一点,靠里面的0 1间距小一点即可
所以当访问数据的时候就需要
1、确定哪一个盘面
2、通过磁头左右摇摆确定数据在盘面上的哪一个磁道
3、通过盘面的旋转确定在哪一块扇区
所以这种磁盘访问数据的效率是根据磁头的摆动确定磁道和盘面的旋转确定扇区决定的
3、磁盘的逻辑结构:
磁盘的一个个扇区可以抽象成磁带一样的线性结构
磁盘的逻辑结构----------线性的
如上,可以将一个个盘面抽象成一段段的长条,我们又知道盘面里面又有磁道
而一个个磁道里面又有扇区
那么我们就可以将这个磁盘抽象成一个基于扇区的数组,任何一个扇区都有下标
4、CHS寻址方式:
假如每一个盘面有20000个扇区,有50个磁道,每个磁道就有400个扇区
那么得到一个扇区编号为36666,那么该怎么计算寻找呢?
1、将扇区编号 36666/20000=1,所以就在第二面盘面上(盘面和扇区是从0开始编号的)
2、将扇区编号 36666%20000=16666,16666/400=41,16666%400=266
所以得到:该扇区的物理地址在:
磁道(cylinder) = 41
磁头(head) = 1
扇区(sector) = 266
这样就把逻辑扇区地址(LAB地址)转化为物理地址(CHS地址),这个逻辑扇区地址是操作系统所认识的,方便定位扇区位置,这种逻辑扇区地址也可以叫做LAB地址
LAB地址是逻辑扇区地址,是一种直接以扇区为单位来寻址的方式,因此也可以称为逻辑扇区地址,在计算机存储系统中,LBA地址用于标识磁盘上的每个扇区,通过逻辑块地址来访问存储设备上的数据
LBA地址与CHS地址(也就是物理地址)是两种不同的寻址方式,CHS地址是通过柱面、磁头和扇区三个参数来定位磁盘上的每个扇区,而LBA地址则是通过逻辑块地址来定位,更加直观和方便,在物理结构上,通常使用CHS来定位扇区,而在逻辑结构上,则使用LBA地址进行定位
不仅仅只有CPU有寄存器,其他外设如磁盘也有寄存器:
比如说控制寄存器,状态寄存器,数据寄存器,地址寄存器等等,这些寄存器在磁盘控制中起着关键作用,确保数据的正确读写和系统的稳定运行
二、inode:
1、磁盘分治:
那么一个磁盘的空间是很大的,所以直接对其进行管理对OS的成本是很大的,所以就需要把这个磁盘进行分区分组,然后对每一个组分别进行管理,如果能够把每一个组管理好,那么也就可以把整个磁盘管理好,这就叫做分治思想管理
例如:下面是一个1TB大小的磁盘
接着对其进行分区,这个操作在Windows中对应的就是C,D,E盘的区别
但是你说分区就分区,操作系统是怎么进行分区的呢?---------操作系统是定义一个数组,这个数组里面有几个元素就可以有几个分区,每一个元素就是一个结构体,每个结构体里面维护两个变量作为start和end,指向一个分区的起始位置和末尾位置,这样就能够对这个磁盘进行分区
struct partion
{
int start;
int end;
}
然后定义一个数组:struct partion part[5],这样就将这个磁盘分为了五个区
但是这一个区仍然还是很大,那么就分别对一个区进行分组
如上,后面蓝色的部分就是一个区里面的一个个组,管理好这一个个组就能够管理好一个个区,进而管理好这个磁盘,
上面绿色的部分叫做boot block(启动块)是Linux文件系统中的一个特殊区域,通常位于分区的第一个块,它的主要作用是存储启动信息,用于在开机时引导操作系统,如果Boot Block中的数据有误或丢失,操作系统可能无法启动,所以boot block对于操作系统的启动很重要
接着对每一个组进行管理:
2、Group:
接下来聊聊组里各个地方代表着什么:
Data Blocks:
这里保存的是一个文件的内容(我们知道,文件=文件属性+文件内容),这里面是以“块”的形式作为最小存储单元,每一个块的大小为4KB,所以,既是我们只写1字节的数据,也要在某个组里面申请4KB(一个块)大小的空间来进行存储,
后面其余部分的就被浪费掉了,但是这样其实不是很浪费空间,因为一个无论多大的文件,最后都只会浪费一个块大小的空间
其中每一个块都有编号
inode Table:
我们知道,文件的属性和内容是分开存储的,inode在Linux中存储了单个文件的所有属性,inode是一个结构体,这个结构体里面包含着文件的各种属性和信息(注意,里面不包括文件名和文件内容)inode Table可以理解为一个数组,这个数组里面每一个元素都是一个inode结构体
struct inode
{
int inode_number//文件inode
//文件类型:如普通文件、目录、符号链接、设备文件
//文件权限:包括文件的读、写、执行权限
//所有者和组:文件的所有者和所属组
//时间戳:包括文件的最后访问时间(atime)、最后修改时间(mtime)、最后状态改变时间(ctime)
//文件大小:文件的实际大小(以字节为单位)
//链接数:表示文件的硬链接数量
int block[NUM];
}
里面有一个描述文件的inode_number,操作系统找到文件就是通过这个inode_number找到的而不是通过文件名,在Linux中可以通过指令在ls 后加-i选项来进行查看文件的inode编号
inode结构体里面存放的是文件的属性,右边的Data Blocks存放的是文件内容,那么这两这有什么联系呢?
在inode结构体中,有一个数组int block[NUM](如下,这里通常是15)
那么在block这个数组里面就会将0~11中填的是右边每一个块的编号,然后就可以根据整形编号找到blocks中对应的块,这样就能够先找到文件的inode编号,在找到文件的文件属性,就可以在属性中找到数组block,进而找到文件内容
但是如果block数组中每一个都填入的整数都对应着文件内容,我们又知道,一般情况下,一个文件只有一个inode,也就是说只有一个block数组,那么那些很大的文件又怎么被找到呢?
这就要用到数组后面的几位作为二级索引,三级索引,比如12位置的地方存储的是150,那么就会在Data blocks中找到对应的块,但是这个块里面存放的不是文件内容,而是其他整数,就可以通过这里的整数找到其他块的编号,这样就可以存储大文件了
那么二级索引和三级索引和多级索引有什么区别呢?
如果是二级索引的话,那么就会先找到Data blocks中的第一个对应的块,然后第一个块中存放的就是其他块的编号,这样就可以通过这个块找到其他块,那么其他块里面放的就是文件内容
如果是三级索引的话,那么首先还是通过这个整数找到Data blocks中的第一个对应的块,然后第一个块中存放的就是其他块的编号,这样就可以通过第一个块找到其他块,其他块里面存放的又是另外块的编号,就会通过其他块找到另外块,这些另外块里面存放的就是文件内容
四级索引,五级索引以此内推
那么操作系统是如何将扇区和块,inode对应起来的呢?
这些块或者是inode最后都会被操作系统解释称扇区,inode和块都有它自己对应的编号,讲这些编号通过相应的算法找到对应的扇区编号
inode bitmap与block bitmap:
inode有这么多编号,blocks也有这么多块,那么我们怎么知道哪些inode被使用了,哪些block被使用了呢?
在每一个组中,有一部分区域是位图结构,比如上述的inode bitmap就将比特位和inode Table映射起来,如果有1.5w多个inode也就有1.5w多个字节数这样换算成KB也就14KB左右,实际上数据是很小的,
block bitmap和inode bitmap是一样的,都是通过比特位的映射0还是1来决定某个编号的inode或者某个编号的块是否能够被使用
group descriptor table:
缩写GDT,组描述符表,描述的是整个分组的使用情况,
存储块组的元信息:GDT存储了关于每个块组的元信息,包括块组中的inode和数据块的位置,以及空闲块和inode的数量等信息
帮助文件系统管理和分配:GDT提供了有关块组的重要信息,帮助文件系统在块组级别进行管理和分配
快速定位inode和数据块:通过GDT,文件系统可以快速定位到块组中的inode表和数据块,从而提高文件操作的效率
inode和数据块的管理:GDT中的每个组描述符(Group Descriptor)包含了inode位图和块位图的起始块号,这些位图用来标识inode和数据块的使用情况,即哪些inode和数据块是空闲的,哪些是已使用的
Super block:
Super block也叫超级块,描述的是整个分区的文件系统的情况,记录了文件系统的类型、大小、状态以及其他元数据结构的信息
这些信息包括inode的数量、block的数量、剩余的block和inode数量、block的大小,边界位置等
所以当Super block损坏后,挂掉的不仅仅是这一小块地方,更是这一整个区,这一整个区的边界就不清楚了,所以Super block是很重要的
但是如果在每一个组中都加上Super block这会导致效率问题(如果删除一个文件或者进行文件操作的时候,不仅仅是Super block也要更新一下,毕竟里面记录了文件系统的许多信息)
但是如果只有一个的话就会导致安全问题
所以一般会在一个分区中的些许组里面进行加入,这样更新成本比较低,并且当一个Super block损坏了之后,其余的Super block仍会起作用
格式化:
每一个分区在被使用之前,必须提前将部分文件系统的属性信息提前设置进对应的分区中,方便我们后续使用这个分区或者分组
如下,左边的四个信息,是对后面两个属性的描述,所以当要使用一个分区的时候要先把左边的四个信息先加载,提前将文件系统的信息写到文件中,这个过程叫做格式化
三、文件相关操作:
有了上述的基础,那我们看看几个问题
1、文件创建:
1、找到一个没有被使用的inode作为新创建文件的inode,将文件的属性填入inode结构体
2、在Data blocks中找到未被使用的块,然后将文件内容填入若干个块
3、记得修改位图映射对应的比特位
2、文件删除:
1、找到待删除文件的inode,这样就能够找到对应位图中的0,1描述
2、将1(文件存在)修改为0(文件删除)
注意:
Linux中文件的删除并不是真的删除了,而是告诉操作系统,这个文件块能够被覆盖了,所以当我们不小心误删了文件,那么最好什么都不要做,找专业的人去帮忙恢复
3、文件查找、修改
首先拿到这个文件的inode,然后根据这个inode看inode bitmap是否是1,如果是就可以在这个inode结构体里面找到int block[NUM]这个数组进而找到对应的文件内容
如果想修改的话就先找到,然后将数据覆盖写入对应块
到现在,肯定会有一个问题:
操作系统是怎么拿到inode编号的呢 并且我们在使用的时候是使用的是文件名而非inode编号
想要理解上述问题,我们得重新理解目录了
在Linux中一切皆是文件,所以目录也是文件,也有它自己的inode,目录也有自己的属性,内容
那么目录的内容也是放在数据块中的,里面放的就是当前目录下文件的文件名和该文件名对应的inode映射关系,这是一个kv结构,所以我们在使用的时候只关心文件名,他会自动找到对应文件的inode
那么我们可以更深入理解之前的问题:
1、为什么同一个目录下不能够有同名文件:
这是因为文件名和文件inode是kv对应关系,如果有相同的文件名就对应不上了
2、目录下,没有w权限,我们无法创建文件,因为没有写权限,这样文件名和文件inode之间的映射关系是无法写入到对应的数据块里面
3、目录下,没有r权限,我们无法查看文件,这是因为如果没有读权限,那么就无法查看该目录下的数据块,自然而然也就看不到文件了
4、目录下,没有x权限,我们就无法进入这个目录,这是因为进入目录的时候,要对环境变量更新,当没有执行权限的时候就无法将环境变量进行更新,也就无法进入目录了,因为列出目录内容需要读取目录项,这需要执行权限
那么目录也是文件,我们如何拿到这个目录的文件的inode呢?
一个目录肯定也是一个目录的子目录,那么一直向上追溯上去,就会得到,所以目录都是根目录的子目录,而我们只需有根目录的inode,就可以找到所有文件的inode了
但是一直递归太慢了,所以就会用到dentry缓存-------dentry缓存用于快速访问之前查找操作的结果,减少对磁盘的直接访问,从而提高文件系统性能
将常用的目录文件保存起来, 以及他路径上的所有的inode以及路径信息缓存起来,用的时候直接访问即可