目录
这里我们知道打开文件就是将文件加载到内存中,我们知道在我们使用的计算机中,内存要远远小于外设的,也因此在操作系统中打开的文件可能是计算机文件的1%或0.1%等。
这里我们知道在操作系统中我们会对打开的文件进行先描述,再组织创建内核数据结构管理起来。那在外存中的那些未打开的文件又是怎么样的呢?
那些未被打开的文件在外设中也是按照一定的规则被管理着的(方便用户即时,快速的打开文件),这也是为什么你在使用打开文件时,只需要用fopen提供路径就可以快速打开文件的原因。
这里我们可以举快递站的例子:
快递站每天都会收到很多快递,这里只有极少数的快递会被拿走(被打开的文件),绝大多数快递会被滞留在快递站里(未被打开的文件)。如果我们不对滞留的快递做管理,会使快递堆积成山。这时当人来拿快递时(打开文件),就会花费很长的时间还不一定能找的到。
这里我们把管理打开的文件与未被打开的文件的过程,叫做文件系统
磁盘是---硬件---物理存储设备,它由于容量大,价格便宜被企业大量使用。
磁盘:主要由磁头与盘面组成。
有多个盘面,盘面的上下两面都可以记录数据。每个面都对应的有一个磁头。
磁盘的盘面可以被读写。
磁头开以左右移动,盘面可以逆时针或顺时针高速旋转(旋转速度很快),通过这样可以定位到盘面上的任意位置。
因此磁盘的读写是机械运动,注定了磁盘是非常慢的。这也是为什么在执行可执行文件时,要把可执行文件预先放到内存中的原因。
注:磁盘里的磁头与磁面没有挨着,只是离得非常近。
磁盘盘面虽然表面光滑,但是由一圈一圈的同心环组成的。这一圈的环,我们叫做磁道。磁道之间也是有一定的距离的。
一个磁道被两条半径所组成的区域,我们叫做扇面(图中红色部分),
这里我们可以知道:
一个盘面可以有很多同心磁道组成
一圈磁道可以有很多的扇区组成
扇区是磁盘在访问时的最小存储单元---一般是512字节
注:如果你想读或写磁道上的数据时,必须把数据所在的扇区全部加载到内存中。也正是因此磁盘也被叫做块设备
每一个磁道所存储的数据大小是一样的,因此里圆心越近的磁道,数据密集程度越密,越远数据越疏。
如何向一个扇区写入数据,我们该如何寻址(定位)?
1.选择那一面---本质是选择那一个磁头
2.选择该面上的哪一个磁道(也叫柱面)
3.选择该磁道的哪一个扇区
这里我们把这种方法叫做CHS定位法(C--cylinder(柱面)。? H--Head(磁头)? S--sector(扇区))
这里我们先那磁带举例说明:
相信大家小时候都玩过磁带里的磁条,我们可以将它拿出来拉成一条直线。这里我们可以类比,将磁盘的盘片想象成为线性空间。
那么整个磁盘可以看作一个sector arr[n]的数组,这样对磁盘数据的管理就变成了对数组的管理,只要知道数组的下标,我们就可以通过计算,算出数据是在那个磁头,磁道与扇区上,通过CHS定位法在磁盘上找到。
对于操作系统,可以按照扇区为单位进行存取,也可以基于文件系统,按照文件块为单位进行数据存取(以8个扇区[4KB]为基本单位--文件块)。做LBA(Logical Block Address).这里LBA也是数组下标,因此我们也可以通过计算与CHS法,完成对磁盘数据的读写,不同的是LBA一次操作8个扇区。
因此:对于存储设备的管理,OS层面,就转换成为了对数组的增删改查!!!
这里我们知道磁盘的内存是比较大的,例如一块500GB的磁盘,我们对磁盘中文件的管理也就是对这500GB空间的管理。这里如果直接对这500GB的空间管理,难度太大,也不利于我们用户的使用。因此对于磁盘的管理,我们采用了分治的策略,将500GB的空间分成5个100GB进行管理,
每个100GB又划分成多个2GB进行管理。这样我们就可以先把这2GB的空间管理好,然后再把成功的管理方法在其他的区域照搬实行。
这里我们把将500G划分成100G的动作叫做分区。(这里对于我们平常使用的笔记本电脑,只有一块磁盘,而我们在电脑上看到的C盘D盘等其实就是把一大块的磁盘分区后,给每个区取的名字)。
那么我们又是如何管理好每一个分区的呢?
这里我们还是觉得100GB的空间有一点大,因此这里我们还继续划分。将100GB划分为一个2GB空间的一个goup(组)。因此对于磁盘的管理我们就简化为了对于这一个组2GB的管理,只要我们把这2GB的空间管理好,其他组就可以照搬管理了。
思想如图:
那么又是如何管理这2GB的空间的呢?
这里Linux系统采用了这样的方法:
我们把每一个分区划分成一个一个的块组(2GB的空间,Block group),这样我们对于2GB的空间管理就变成了对块组进行管理,而在块组中又有如下部分。
Boot Block :我们叫做启动块。一般启动块会在整个磁盘中编号为0的磁头,编号为0的盘面,编号为0的磁道的第一个扇区,与计算机启动有关。
这里要想解释后面的五个我们要先补充一些知识:
一,我们要知道,磁盘存储文件本质是:存储文件信息(文件内容+属性),文件管理的数据。这些对于磁盘来说都是数据,在Linux中采用了内容与属性分开存储。?同时Linux规定在在使用磁盘分区时,先要让管理数据写入块组中,这个工作我们叫做格式化。
在Linux中找到一个有文件的目录
然后使用两个指令:
ll 与 ls -li
这里我们发现在使用ls -li指令时,多出来了一部分数字,在Linux中我们把他们叫做?inode编号,一般情况下一个文件只有一个inode编号,基本上每一个文件都必须有。inode编号在一个分区里具有唯一性。Linux内核中,识别文件,和文件名无关,只和inode有关。
接下来先解释就开始解释。
:?存放文件属性 如 文件大小,所有者,最近修改时间等.
这里我们要知道,文件属性的种类与大小时确定的,不会发生变化。因此在磁盘中保存文件属性是通过创建一个inode结构体保存。也就是说一个inode对象里就存储着一个文件的属性。
struct inode
{
//文件大小
//权限
//拥有者
//所属组
//inode编号
// ...
}
在Linux中这个结构体的大小是固定的:128字节。
因此正如其名一样,inode Table中存储着很多个inode。当我们知道inode Table的大小时,我们就可以计算出inode Table中最多可以存放多少个文件。,并且inode在表中的位置就是在块组中的inode编号,因此在表中也很容易找到inode在表中的位置:inode编号*128。所以inode Table 可以看成struc inode inode_table[n](struct inode为元素的一个数组)。?
这里可能有人会问,如果像上面那样,在一个分区里inode编号不是就有可能重复了吗?
这里其实是这样的:
在Linux系统中,会在每一个块组的最前面加上一个start_inode_number。块组里的inode编号+start_inode_number就是使在一个分区里的每一个inode编号不同。这也是最终在分区上的inode编号。如果你想知道你在inode Table表中的位置也很简单,只需要拿着我们最终使用的inode编号 - 对应的start_inode_number,就可以知道inode是表中的第几个。
:数据区,存放文件内容
从上面我们知道文件的属性与内容是分别存储的,属性存在inode Table中,而Data Block就是用来存数据的。
它里面是以4KB为单位的数据块区域
这里的每一个数据块都保存的是整个块组里所有文件的内容数据。为了便于找到对应的数据,每一个数据块也都应该有一个编号。
为了我们方便找到一个文件对应的数据块,因此struct inode里有一个数组 int blocks[N] (N一般是15)。这个数组记录的是文件内容在Data Block里对应的数据块的块号。如:当一个文件的内容被放在了1,2,4这三个数据块中。那么文件对应的inode结构体里的block数组中记录的值就是1,2,4。
这里可能有人会问,如果这样那么一个文件内容大小最多只能是15*4KB,那写几个GB的文件又是怎么来的呢?
这里我们要说block数组采用了这样的策略,前13个元素是直接映射,第14个采用二级映射,第15个采用三级映射。
直接映射:block数组记录的数据块直接存储文件数据。空间大小:13*4KB
二级映射:block数组记录的数据块存储的是数据块号,再接着找到的数据块才存储数据。空间大小:4KB/4*4KB。
三级映射:block数组记录的数据块存储的是数据块号,再接着找到的数据块还是存储的数据块号号,再找到的数据块才是存储的数据。空间大小:4KB/4*4KB/4*4KB。
如果块组中的数据块全部用完还是还没存储完文件数据,那么可以同一分区里其他块组的数据块:块组中记录其他分区的区号与所用的数据块。(注:可以跨块组使用数据块,但不能跨分区)
了解到这里,未来当我们需要读取一个文件时,我们只需要知道这个文件的inode,就可以获取文件的各种属性,再通过inode里的block数组里的内容,到Data Block里找到对应编号的数据块,就可以将数据库的内容导入到内存中,就获取文件内容。同时这样文件的属性与内容就关联了起来。
那么我们又怎么找到这个文件的inode呢?其实最终还是落到了找到这个文件的inode编号上(在整个分区上具有唯一性)。
所以当我们找到inode编号,先减去start_inode_number,定位是在那个块组里,然后再在inode Table找到inode,找到文件属性,通过bloc数组内容,在Data Block中找到文件内容,最后将文件的属性与内容加载到内存中,就从磁盘中读取出来。
: 通过位图,用一个bit来表示inode是否空闲可用。
这里我们知道当我们创建一个文件时,我们就要在inode表里给它分配一个inode,那么我们如何知道表里面那个inode不可用,那个可以用呢?这里就是用位图来解决的。
这里我们用位图中bit的个数来表示inode Table最多又几个inode,用0来表示该inode可以使用,用1表示该inode不可以使用。
通过遍历这个位图,知道那写inode不可以使用,那些可以使用。然后通过合适的算法,分配一个该创建的文件。因此inode Bitmap管理着inode Table.
:Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
这里同inode Bitmap相同,用位图来管理标记数据块的使用情况。
在创建文件时,遍历Block Bitmap,用合适的算法分配合适的数据块给文件存储数据。Block Bitmap管理着Data Block
因此从我们可以得知:?inode Table与Data Block存储着文件信息,inode Bitmap与Block Bitmap存储着因管理文件而产生的数据。
根据文件inode编号找到inode结构体,获取block数组里的数据,将Block Bitmap中对应的对应的bit位置为0。用相同的方法可以将inode Bitmap中对应的bit位置为0。(因为在inode BItmap与Block Bitmap中0表示inode与数据块为空,可用。当其再次使用时,只需将新文件的属性与内容覆盖即可)
因此删除文件只是消除管理文件的数据,并不会立即就删除文件的属性与内容。
同时这也是我们为什么能进行数据恢复的原因:
在Linux系统中有一个日志文件,可以记录被删除文件的inode编号,这样我们就可以得知其用了那个inode与数据块。然后将其在inode Bitmap与Block Bitmap中对应的bit位置为1。(要在这个inode编号再次被利用前进行数据恢复,因为一旦被再次利用,被删除文件的属性内容就会被覆盖)
同时根据创建与删除文件的过程我们也可以知道,为什么下载文件要远远慢于删除文件:
下载文件要分配inode查找可用的inode与数据库,再把文件的属性与内容拷贝到上面。
删除文件,只需用inode找到其对应在inode Bitmap与Block Bitmap中的bit位置为0即可。
:块组描述符,描述块组属性信息(使用情况)
主要记录了有多少个inode,多少个inode被使用。有多少个数据块,多少个数据块被使用了等块组信息,用来衡量整个块组的使用情况。
注:Super Block不属于块组。可能只有几个块组有,不是每个一个都有的。
为什么Super Block不想GDT一样每一个块组都有或是只有一个,而是在几个块组才有?
这里我们知道GDT是用来存储一个块组的信息,Super Block是用来存储一个分区的信息。
首先GDT管理的空间较小,如何一个GDT挂掉,对于计算机整体而言影响并不是很大。而Super Block 管理的空间很大,如果只有一个,它挂掉了,就会导致电脑中这个盘无法使用。如果是C盘的挂掉了,就是导致这台电脑无法使用。因此为了防止,才在一个分区用多个Super Block。同时每一个分区只需要一个就行了,如果每一个块组都有一个,就会有很大的冗余,浪费空间。
? ?
这里还是根据操作系统的六字真言:先描述,再组织。
在内核里会维护一张文件系统列表(一个结构体对象):它使用来存储每一个分区的Super Block。因此在操作系统软件上对整个磁盘的管理就变成了对几个Super Block的管理?
文件系统只用inode编号来找到文件对其进行操作的,但是用户使用时一直用的都是文件名。
因此在系统中文件名要与inode有一个映射关系。
那这个映射关系又是如何做到的呢?这里我们就要先理解一下目录。
?首先目录也是一个文件,因此目录有自己的inode与数据块,也是有它的属性与内容。这里目录的属性与普通文件一样。而目录的数据块中存储着文件名与该文件inode的映射关系(每个文件名对应的inode)。
这也解释了为什么一个目录下不 能出现同名文件,因为一个文件名只有一个inode。?
同时也解释了为什么我们可以通过一个文件名就可以找到磁盘中的文件:我们可以在文件所在目录在目录的数据块找到文件名对应的inode编号,就可以找到文件,将文件导入内存。
在Linux中在一个目录下,新建,删除,修改一个文件对于这个目录我需要什么权限呢? 当然是w权限。
那么今天我们知道,这里的新建,删除,修改本质是修改目录文件的数据块内容,就是对文件进行修改,因此需要w权限。
这里我们所说的文件系统内容都是在一个分区里进行的,但是我们知道在磁盘中,是要先确定分区的,那么在Linux中又是如何做到的呢?
Linux中是通过挂载,使用指令
sudo mount /dev/sdb1 (分区)/mnt(目录)
将一个目录(我们常用的C盘,D盘等)与分区关连起来,可以通过路径的前缀得知是在那个分区的。
因此在我们系统获得一个路径时,先通过路径前缀,确定在那个分区,然后再在前缀目录的数据块里找到下一级目录对应的inode,然后按照这个方法遍历路径到底, 在磁盘中找到文件对应的inode,然后将用文件的属性与内容加载到内存中。