33 | 仓库划分:文件系统的格式化操作
下载APP
关闭
渠道合作
推荐作者
33 | 仓库划分:文件系统的格式化操作
2021-07-23 LMOS 来自北京
《操作系统实战45讲》
课程介绍
讲述:陈晨
时长14:12大小12.97M
你好,我是 LMOS。
上一节课中,我们已经设计好了文件系统数据结构,相当于建好了仓库的基本结构。
今天,我将和你一起探索仓库的划分,即什么地方存放仓库的管理信息,什么地方存放进程的“劳动成果”(也就是文件),对应于文件系统就是文件系统的格式化操作。
具体我是这样安排的,我们先来实现文件系统设备驱动,接着建立文件系统超级块,然后建立根目录,最后建立文件系统的位图。下面,我们先从建立文件系统设备开始。
文件系统设备
根据我们前面的设计,文件系统并不是 Cosmos 的一部分,它只是 Cosmos 下的一个设备。
既然是设备,那就要编写相应的设备驱动程序。我们首先得编写文件系统设备的驱动程序。由于前面已经写过驱动程序了,你应该对驱动程序框架已经很熟悉了。
我们先在 cosmos/drivers/ 目录下建立一个 drvrfs.c 文件,在里面写下文件系统驱动程序框架代码,如下所示。
这个框架代码我们已经写好了,是不是感觉特别熟悉?这就是我们开发驱动程序的规范操作。下面,我们来建立文件系统设备。
按照之前的设计(如果不熟悉可以回顾第 32 课),我们将使用 4MB 内存空间来模拟真实的储存设备,在建立文件系统设备的时候分配一块 4MB 大小的内存空间,这个内存空间我们用一个数据结构来描述,这个数据结构的分配内存空间的代码如下所示。
上述代码中,new_rfsdevext_mmblk 函数分配了一个内存空间和一个 rfsdevext_t 结构实例变量,rfsdevext_t 结构中保存了内存空间的地址和大小。而 rfsdevext_t 结构的地址放在了 device_t 结构的 dev_extdata 字段中。
剩下的就是建立文件系统设备了,我们在文件系统驱动程序的 rfs_entry 函数中,通过后面这段代码完成这个功能。
其实这和我们之前的写 systick 驱动程序的套路差不多,只不过这里需要分配一个模拟储存设备的空间,并把它放在 device_t 结构相关的字段中。还有很重要的一点是,这个设备类型我们要在 rfs_set_device 函数把它设置好,设置成文件系统类型。
需要注意的是要把 rfs_entry 函数放在驱动表中,文件系统程序才可以运行,下面我们就把这个 rfs_entry 函数,放入驱动表中,代码如下所示。
有了上述代码,Cosmos 在启动的时候,在 init_krldriver 函数中就会运行 rfs_entry 函数。从名字就能看出 rfs_entry 函数的功能,这是 rfs 文件系统设备驱动程序的入口函数,它一旦执行,就会建立文件系统设备。
文件系统系统格式化
我们经常听说格式化硬盘、格式化 U 盘,可以把设备上的数据全部清空,事实是格式化操作并不是把设备上所有的空间都清零,而是在这个设备上重建了文件系统用于管理文件的那一整套数据结构。这也解释了为什么格式化后的设备,还能通过一些反删除软件找回一些文件。
在储存设备上创建文件系统,其实就是执行这个格式化操作,即重建文件系统的数据结构。
那么接下来,我们就从建立文件系统的超级块开始,然后建立用于管理储存设备空间的位图,最后建立根目录,这样才能最终实现在储存设备上创建文件系统。
建立超级块
我们首先来建立文件系统的超级块。建立超级块其实非常简单,就是初始化超级块的数据结构,然后把它写入到储存设备中的第一块逻辑储存块。
下面我们一起写代码来实现,如下所示。
上述代码的意思是,我们先在内存缓冲区中建立文件系统的超级块,最后会调用 write_rfsdevblk 函数,把内存缓冲区的数据写入到储存设备中。
下面我们来实现这个 write_rfsdevblk 函数,代码如下所示。
前面我们一下子写了三个函数,由于我们用内存模拟储存设备,我们要写一个 ret_rfsdevext 函数返回设备扩展数据结构,这个函数和 ret_rfsdevblk 函数将会一起根据块号,计算出内存地址。然后,我们把缓冲区的内容复制到这个地址开始的内存空间就行了。
建立位图
接下来,我们要建立文件系统的位图了。
延续我们文件系统的设计思路,储存设备被分成了许多同等大小的逻辑储存块,位图就是为了能准确地知道储存设备中,哪些逻辑储存块空闲、哪些是被占用的。
我们使用一个逻辑储存块空间中的所有字节,来管理逻辑储存块的状态。建立位图无非就是把储存设备中的位图块清零,因为开始文件系统刚创建时,所有的逻辑储存块都是空闲的。下面我们来写好代码。
这里为什么又多了几个辅助函数呢?这是因为,位图块的块号和储存介质的逻辑储存块总数,都保存在超级块中,所以要实现获取、释放超级块的函数,还需要一个读取逻辑储存块的函数,写入逻辑储存块的函数前面已经写过了。
因为第 0 块是超级块,第 1 块是位图块本身,所以代码从缓冲区中的第 3 个字节开始清零,一直到 devmaxblk 个字节,devmaxblk 就是储存介质的逻辑储存块总数。缓冲区中有 4096 个字节,但 devmaxblk 肯定是小于 4096 的,所以 devmaxblk 后面的字节全部为 1,这样就不会影响到后面分配逻辑储存块代码的正确性了。
最后,我们把这个缓冲区中的数据写入到储存介质中的第 bitmapblk 个逻辑储存块中,就完成了位图的建立。
建立好了管理逻辑储存块状态的位图,下面就去接着建立根目录吧!
建立根目录
一切目录和文件都是存放在根目录下的,查询目录和文件也是从这里开始的,所以文件系统创建的最后一步就是创建根目录。
根目录也是一种文件,所以要为其分配相应的逻辑储存块,因为根目录下的文件和目录对应的 rfsdir_t 结构,就是保存在这个逻辑储存块中的。
因为根目录是文件,所以要在这个逻辑储存块的首个 512 字节空间中建立 fimgrhd_t 结构,即文件管理头数据结构。最后,我们要把这个逻辑储存块的块号,储存在超级块中的 rfsdir_t 结构中,同时修改该 rfsdir_t 结构中的文件名称为“/”。
要达到上述功能要求,就需要操作文件系统的超级块和位图,所以我们要先写好这些辅助功能函数,实现获取 / 释放位图块的代码如下所示。
获取 / 释放位图块非常简单,就是根据超级块中的位图块号,把储存设备中的位图数据块读取到缓冲区中,而释放位图块则需要把缓冲区的数据写入到储存设备对应的逻辑块中。获取 / 释放超级块的函数,我们建立位图时已经写好了。
建立根目录需要分配新的逻辑储存块,分配新的逻辑储存块其实就是扫描位图数据,从中找出一个空闲的逻辑储存块,下面我们来写代码实现这个函数,如下所示。
rfs_new_blk 函数会返回新分配的逻辑储存块号,如果没有空闲的逻辑储存块了,就会返回 0。下面我们就可以建立根目录了,代码如下。
上述代码的注释已经很清楚了,虽然代码有点长,但总体流程还是挺清晰的。首先,分配一块新的逻辑储存块。接着,设置超级块中的 rfsdir_t 结构中的名称以及类型和块号。然后设置文件管理头,由于根目录是目录文件,所以文件管理头的类型为 FMD_DIR_TYPE,表示文件数据存放的是目录结构。最后,回写对应的逻辑储存块即可。
串联
建立超级块、建立位图、建立根目录的代码已经写好了。
现在我们来写一个 rfs_fmat 函数,把刚才这三个操作包装起来,调用它们完成文件系统格式化这一流程。顺便,我们还可以把 init_rfs 函数也实现了,让它调用 rfs_fmat 函数,随后 init_rfs 函数本身会在 rfs_entry 函数的最后被调用,代码如下所示。
上述代码中,init_rfs 函数会在 rfs 驱动程序入口函数的最后被调用,到这里我们 rfs 文件系统的格式化操作就完成了,这是实现文件系统的重要一步。
测试文件系统
尽管我们的文件系统还有很多其它操作,如打开、关闭,读写文件,这些文件相关的操作我们放在下一节课中来实现。这里我们先对文件系统格式化的功能进行测试,确认一下我们的格式化代码没有问题,再进行下一步的开发。
测试文件系统超级块
之前我们文件系统格式化操作的第一步,就是建立文件系统的超级块。
所以我们首先来测试一下建立文件系统超级块的代码,测试方法非常简单,我们只要把超级块读取到一个缓冲区中,然后把其中一些重要的数据,打印出来看一看就知道了,我们写个函数完成这个功能,代码如下所示。
测试代码我们已经写好了,下面我们打开终端,切换到 Cosmos 目录下执行 make vboxtest,Cosmos 加载 rfs 驱动程序运行后的结果,如下所示。
文件系统超级块测试.
上图中我们可以看到,文件系统的标识、版本和最初定义的是相同的,逻辑储存块的大小为 4KB。位图占用的是第 1 个逻辑储存块,因为第 0 个逻辑储存块被超级块占用了。
同时,我们还可以看到储存设备上共有 1024 个逻辑储存块,根目录文件的逻辑储存块为第 2 块,名称为“/”,这些正确的数据证明了建立超级块的代码是没有问题的。
测试文件系统位图
测试完了文件系统超级块,我们接着来测试文件系统位图。测试方法很简单,先读取位图块到一个缓冲区中,然后循环扫描这个缓冲区,看看里面有多少个为 0 的字节,即表明储存介质上有多少个空闲的逻辑储存块。
我们一起来写好这个测试函数,代码如下所示。
test_rfs_bitmap 函数我们已经写好了,别忘了在 rfs_entry 函数的末尾调用它,随后我们在终端下执行 make vboxtest,就可以看到 Cosmos 加载 rfs 驱动程序运行后的结果,如下所示。
文件系统位图测试
上图中的空闲块数为 1021,表示储存介质上已经分配了 3 块逻辑储存块了。这就证明了我们建立文件系统位图的代码是没有问题的。
测试文件系统根目录
最后我们来测试文件系统的根目录文件建立的对不对,测试方法就是先得到根目录文件的 rfsdir_t 结构,然后读取其中指向的逻辑储存块到缓冲区中,最后把它们的数据打印出来。
这个函数很简单,我们来写好它,代码如下。
test_rfs_rootdir 函数同样要在 rfs_entry 函数的末尾调用,然后我们在终端下执行 make vboxtest,就可以看到 cosmos 加载 rfs 驱动程序运行后的结果了。
文件系统根目录测试
从上图我们可以看到,根目录文件的类型为目录文件类型。因为根目录文件才刚建立,所以文件大小为 0,文件数据的存放位置从文件占用的第 1 块逻辑储存块的 512 字节处开始。因为第 0、1 块逻辑储存块被超级块和位图块占用了,所以根目录文件占用的逻辑储存块,就是第 2 块逻辑储存块,只占用了 1 块。
好了,上面一系列的测试结果,表明我们的文件系统格式化的代码正确无误,文件系统格式化操作的内容我们就告一段落了
重点回顾
今天的课程就到这里了,今天我们继续推进了文件系统的进度,实现了文件系统的格式化操作,我来为你把今天的课程重点梳理一下。
首先实现了文件系统设备驱动程序框架,这是因为我们之前的架构设计,把文件系统作为 Cosmos 系统下的一个设备,这有利于扩展不同的文件系统。
然后我们实现了文件系统格式化操作,包括建立文件系统超级块、位图、根目录操作,并且将它们串联在一起完成文件系统格式化。
最后是对文件系统测试,我们通过打印出文件系统超级块、位图还有根目录的相关数据来验证,最终确认了我们文件系统格式化操作的代码是正确的。
虽然我们实现了文件系统的格式化,也对其进行了测试,但是我们的文件系统还是不能存放文件,因为我们还没有实现操作文件相关的功能,下一节课我们继续探索。
思考题
请问,建立文件系统的超级块、位图、根目录的三大函数的调用顺序可以随意调换吗,原因是什么?
欢迎你在留言区记录你的疑问或者收获,积极输出有利于你深入理解这节课的内容。同时,也欢迎你把这节课转给身边的同事、朋友。
好,我是 LMOS,我们下节课见!
分享给需要的人,Ta购买本课程,你将得20元
生成海报并分享
赞 5
提建议
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
上一篇
32 | 仓库结构:如何组织文件?
下一篇
34 | 仓库管理:如何实现文件的六大基本操作?
精选留言(6)
- neohope置顶2021-08-07二、文件系统初始化 1、文件系统本身是个驱动,同样需要把驱动放到全局驱动列表中 osdrvetytabl={systick_entry,rfs_entry,NULL} 2、从而,让系统启动时自动加载驱动 hal_start->init_krl->init_krldriver->rfs_entry new_device_dsc,分配内存 new_rfsdevext_mmblk,分配设备内存 krldev_add_driver,处理驱动 krlnew_device,处理设备 init_rfs->rfs_fmat,初始化文件系统 3、其中,主要逻辑是在rfs_fmat中实现的: A、create_superblk->rfssublk_t_init->rfsdir_t_init,创建超级块。其中初始化超级块时可以看到: 超级块在第0个逻辑块,位图在第1个逻辑块,根目录为空 B、create_bitmap 标记前3个逻辑块为已占用,后续逻辑块为可用 C、create_rootdir 一方面在超级块中标明,根目录在第2块 另一方面,对根目录进行初始化,写入 fimgrhd_t文件管理头,后续有文件就要在这个文件管理头后面依次增加rfsdir_t结构 三、逻辑块使用 1、申请逻辑块 A、读取超级块,从而定位到位图块 B、读取位图块 C、位图中找到第一个可用逻辑块,并设置为使用,并返回该字节对应的逻辑块号 2、归还逻辑块 A、读取超级块,从而定位到位图块 B、读取位图块 C、位图中找到对应的逻辑块,并设置为空闲展开
作者回复: 是的
4 - 青玉白露2021-08-01当然不行,从代码上可以看出来,这三者是相互依赖的,位图需要超级块,建立根目录需要位图来获取空闲的地址,差了哪一步都不行!
作者回复: 哈哈 正确
3 - pedro2021-07-23不能,位图初始化需要操作超级块,根目录初始化需要操作超级块和位图
作者回复: 铁子6666666
1 - 艾恩凝2022-05-1132 33 一直带着问题去学习,我想答案会在34节出现,打卡
作者回复: 找到答案了吗
- ifelse2022-02-22不能,有先后顺序
作者回复: 是的
- 许少年2021-09-23请问老师,如果我想为apfs编写windows驱动,目前掌握了windows内核驱动开发。 我认为文件系统就是操作物理设备空间上的数据与元数据,那么接下来该如何做呢? apfs这块不是很懂。同样的问题,让windows支持ext3。
作者回复: 你写NT文件系统过滤型驱动就行了