提交 392b78db 编写于 作者: Y Yifan Wu

Add code-intro of ch7: easy-fs layer1-2

上级 75a52115
......@@ -265,6 +265,9 @@ GDB 调试支持
运行 rCore-Tutorial-v3
------------------------------------------------------------
在 Qemu 平台上运行
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
如果是在 Qemu 平台上运行,只需在 ``os`` 目录下 ``make run`` 即可。在内核加载完毕之后,可以看到目前可以用的
应用程序。 ``usertests`` 打包了其中的很大一部分,所以我们可以运行它,只需输入在终端中输入它的名字即可。
......@@ -272,6 +275,9 @@ GDB 调试支持
之后,可以先按下 ``Ctrl+A`` ,再按下 ``X`` 来退出 Qemu。
在 K210 平台上运行
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
如果是在 K210 平台上运行则略显复杂。
首先,我们需要将 MicroSD 插入 PC 来将文件系统镜像拷贝上去。
......
......@@ -229,11 +229,11 @@
第一步是能够写出与文件访问相关的应用。这里是参考了Linux的创建/打开/读写/关闭文件的系统调用接口,力图实现一个 :ref:`简化版的文件系统模型 <fs-simplification>` 。在用户态我们只需要遵从相关系统调用的接口约定,在用户库里完成对应的封装即可。这一过程我们在前面的章节中已经重复过多次,读者应当对其比较熟悉。其中最为关键的是系统调用可以参考 :ref:`sys_open 语义介绍 <sys-open>` ,此外我们还给出了 :ref:`测例代码解读 <filetest-simple>` 。
第二步就是要实现 easyfs 文件系统了。由于Rust语言的特点,我们可以在用户态实现 easyfs 文件系统,并在用户态完成文件系统功能的基本测试后,就可以不修改就嵌入到操作系统内核中。我们按照自底向上方的执行流程来介绍easyfs文件系统的具体实现。当然,有了文件系统的具体实现,还需要对上一章的操作系统内核进行扩展,实现与 easyfs 文件系统对接的接口,这样才可以让操作系统拥有一个简单可用的文件系统。带有文件系统的操作系统就可以提高应用开发体验和程序执行与互操作的灵活性,让应用获得文件系统带了的各种便利
第二步就是要实现 easyfs 文件系统了。由于 Rust 语言的特点,我们可以在用户态实现 easyfs 文件系统,并在用户态完成文件系统功能的基本测试并基本验证其实现正确性之后,就可以放心的将该模块嵌入到操作系统内核中。当然,有了文件系统的具体实现,还需要对上一章的操作系统内核进行扩展,实现与 easyfs 文件系统对接的接口,这样才可以让操作系统拥有一个简单可用的文件系统。从而,内核可以支持允许文件读写功能的更复杂的应用,在命令行参数机制的加持下,可以进一步提升整个系统的灵活性,让应用的开发和调试变得更为轻松
easyfs文件系统的设计实现有五层。它的最底层就是对块设备的访问操作接口。为了实现easyfs文件系统,首先需要定义 ``BlockDevice`` trait,其成员函数定义 ``read_block`` 和 ``write_block`` 是操作系统内核中的块设备驱动需要实现的函数。这样就可以把内核中的块设备驱动与easyfs文件系统进行对接。完成对接后,easyfs文件系统可以通过这两个函数对块设备进行读写。
easyfs 文件系统的整体架构自下而上可分为五层。它的最底层就是对块设备的访问操作接口。在 ``easy-fs/src/block_dev.rs`` 中,可以看到 ``BlockDevice`` trait 代表了一个抽象块设备,该 trait 仅需求两个函数 ``read_block`` 和 ``write_block`` ,分别代表将数据从块设备读到内存中的缓冲区中,或者将数据从内存中的缓冲区写回到块设备中,数据需要以块为单位进行读写。easy-fs 库的使用者需要负责为它们看到的实际的块设备具体实现 ``BlockDevice`` trait 并提供给 easy-fs 库的上层,这样的话 easy-fs 库的最底层就与一个具体的执行环境对接起来了。至于为什么块设备层位于 easy-fs 的最底层,是因为文件系统仅仅是在块设备上存储的结构稍微复杂一点的数据,但无论它的操作变换如何复杂,从块设备的角度终究可以被分解成若干次块读写。
而具体使用这两个函数的是自底向上的第二层 -- 块缓存。块缓存是把应用要访问的数据放到一块内存区域中,减少磁盘读写的次数,提高系统性能。块缓存通过 ``read_block`` 和 ``write_block`` 函数接口来读写磁盘数据。这些磁盘数据会缓存在内存中。表示块缓存的数据结构是 ``BlockCache`` 。当我们创建一个 ``BlockCache`` 的时候,将触发一次 ``read_block`` 函数调用,将一个块上的数据从磁盘读到块缓冲区中。由于缓存磁盘块的内存有限,我们需要实现缓存的替换,这就需要实现类似与页替换算法的缓存替换算法。为了简单,我们实现的是FIFO缓存替换算法。具体替换过程是块缓存全局管理器 ``BlockCacheManager`` 中的成员函数 ``get_block_cache`` 来完成的
尽管在最底层我们就已经有了块读写的能力,但从编程方便性和性能的角度,仅有块读写这么基础的底层接口是不足以实现如此复杂的文件系统的,虽然它已经被我们大幅简化过了。比如,将一个块的内容读到内存的缓冲区,对缓冲区进行修改,并尚未写回的时候,如果由于编程上的不小心再次将该块的内容读到另一个缓冲区,而不是使用已有的缓冲区,这将会造成不一致问题。此外还有可能增加很多不必要的块读写次数,大幅降低文件系统的性能。因此,通过程序自动而非程序员手动对块的缓冲区进行统一管理也就势在必行了,该机制被我们抽象为 easy-fs 自底向上的第二层,即块缓存层。在 ``easy-fs/src/block_cache.rs`` 中, ``BlockCache`` 代表一个被我们管理起来的块的缓冲区,它带有缓冲区本体以及块的编号等信息。当它被创建的时候,将触发一次 ``read_block`` 将数据从块设备读到它的缓冲区中。接下来只要它驻留在内存中,便可保证对于同一个块的所有操作都会直接在它的缓冲区中进行而无需额外的 ``read_block`` 。块缓存管理器 ``BlockManager`` 在内存中管理有限个 ``BlockCache`` 并实现了类似 FIFO 的缓存替换算法,当一个块缓存被换出的时候视情况可能调用 ``write_block`` 将缓冲区数据写回块设备。总之,块缓存层对上提供 ``get_block_cache`` 接口来屏蔽掉相关细节,从而可以透明的读写一个块
有了块缓存,我们就可以在内存中方便地处理easyfs文件系统在磁盘上的各种数据了,这就是第三层文件系统的磁盘数据结构。easyfs文件系统中的所有需要持久保存的数据都会放到磁盘上,这包括了管理这个文件系统的 **超级块 (Super Block)**,管理空闲磁盘块的 **索引节点位图区** 和 **数据块位图区** ,以及管理文件的 **索引节点区** 和 放置文件数据的 **数据块区** 组成。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册