uCore-Tutorial-Guide-2023S/source/chapter6/0intro.rst

138 lines
7.0 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

引言
=========================================
本章导读
-----------------------------------------
文件的最早起源于我们需要把数据持久保存在 **持久存储设备** 上的需求。
大家不要被 **持久存储设备** 这个词给吓住了这就是指计算机远古时代的卡片、纸带、磁芯、磁鼓和现在还在使用的磁带、磁盘、硬盘还有近期逐渐普及的U盘、闪存、固态硬盘 (SSD, Solid-State Drive)等存储设备。我们可以把这些设备叫做 **外存** 。在此之前我们仅使用一种存储,也就是内存(或称 RAM。相比内存持久存储设备的读写速度较慢容量较大但内存掉电后信息会丢失外存在掉电之后并不会丢失数据。因此将需要持久保存的数据从内存写入到外存或是从外存读入到内存是应用和操作系统必不可少的一种需求。
.. note::
文件系统在UNIX操作系统有着特殊的地位根据史料《UNIX: A History and a Memoir》记载1969年Ken ThompsonUnix的作者在贝尔实验室比较闲写了PDP-7计算机的磁盘调度算法来提高磁盘的吞吐量。为了测试这个算法他本来想写一个批量读写数据的测试程序。但写着写着他在某一时刻发现这个测试程序再扩展一下就是一个文件系统了再再扩展一下就是一个操作系统了。他的自觉告诉他他离实现一个操作系统仅有 **三周之遥** 。一周写代码编辑器一周写汇编器一周写shell程序在写这些程序的同时需要添加操作系统的功能如 exec等系统调用以支持这些应用。结果三周后为测试磁盘调度算法性能的UNIX雏形诞生了。
本章我们将实现一个简单的文件系统 -- easyfs能够对 **持久存储设备** (Persistent Storage) 这种 I/O 资源进行管理。对于应用访问持久存储设备的需求,内核需要新增两种文件:常规文件和目录文件,它们均以文件系统所维护的 **磁盘文件** 形式被组织并保存在持久存储设备上。
同时,由于我们进一步完善了对 **文件** 这一抽象概念的实现,我们可以更容易建立 ” **一切皆文件** “ (Everything is a file) 的UNIX的重要设计哲学。我们可扩展与应用程序执行相关的 ``exec`` 系统调用加入对程序运行参数的支持并进一步改进了对shell程序自身的实现加入对重定向符号 ``>````<`` 的识别和处理。这样我们也可以像UNIX中的shell程序一样基于文件机制实现灵活的I/O重定位和管道操作更加灵活地把应用程序组合在一起实现复杂功能。
实践体验
-----------------------------------------
获取本章代码:
.. code-block:: console
$ git checkout ch6
在 qemu 模拟器上运行本章代码:
.. code-block:: console
$ make test BASE=1
>> ch6b_usertest
.. code-block::
>> ch6b_filetest_simple
file_test passed!
Shell: Process 2 exited with code 0
>>
它会将 ``Hello, world!`` 输出到另一个文件 ``filea`` ,并读取里面的内容确认输出正确。我们也可以通过命令行工具 ``ch6b_cat`` 来查看 ``filea`` 中的内容:
.. code-block::
>> ch6b_cat
Hello, world!
Shell: Process 2 exited with code 0
>>
本章代码树
-----------------------------------------
.. code-block:: bash
.
├── bootloader
│ └── rustsbi-qemu.bin
├── LICENSE
├── Makefile
├── nfs (新增,辅助程序,要来将 .bin 打包为 os 可以识别的文件镜像)
│ ├── fs.c
│ ├── fs.h
│ ├── Makefile
│ └── types.h
├── os
│ ├── bio.c (新增IO buffer 的实现)
│ ├── bio.h
│ ├── console.c
│ ├── console.h
│ ├── const.h
│ ├── defs.h
│ ├── entry.S
│ ├── fcntl.h (新增,文件相关的一些抽象)
│ ├── file.c (更加完成的文件操作)
│ ├── file.h (更加完成的文件定义)
│ ├── fs.c (新增,文件系统实际逻辑)
│ ├── fs.h
│ ├── kalloc.c
│ ├── kalloc.h
│ ├── kernel.ld
│ ├── kernelvec.S
│ ├── link_app.S
│ ├── loader.c
│ ├── loader.h
│ ├── log.h
│ ├── main.c
│ ├── plic.c (新增,用来处理磁盘中断)
│ ├── plic.h (新增,用来处理磁盘中断)
│ ├── printf.c
│ ├── printf.h
│ ├── proc.c
│ ├── proc.h
│ ├── riscv.h
│ ├── sbi.c
│ ├── sbi.h
│ ├── string.c
│ ├── string.h
│ ├── switch.S
│ ├── syscall.c
│ ├── syscall.h
│ ├── syscall_ids.h
│ ├── timer.c
│ ├── timer.h
│ ├── trampoline.S
│ ├── trap.c
│ ├── trap.h
│ ├── types.h
│ ├── virtio_disk.c (新增,用来处理磁盘中断)
│ ├── virtio.h (新增,用来处理磁盘中断)
│ ├── vm.c
│ └── vm.h
├── README.md
├── scripts
│ └── initproc.py (弱化的 pack.py仅仅用来插入 INIT_PROC 符号)
└── user
本章代码导读
-----------------------------------------------------
本章涉及的代码量相对较多且与进程执行相关的管理还有直接的关系。其实我们是参考经典的UNIX基于索引的文件系统设计了一个简化的有一级目录并支持创建/打开/读写/关闭文件一系列操作的文件系统也就是说本章。本章采用的文件系统和ext4文件系统比较类似。其中也涉及到了inode这个概念。进入本章之后我们的测例文件一开始是存放在我们生成的“磁盘”上的需要我们实现磁盘的读写来进行操作了。我们实现了一个简单的 nfs 文件系统,具体的结构将在下面的章节中说明。大家可以看一看我们本章对 makefile 文件的改动.
.. code-block:: Makefile
QEMU = qemu-system-riscv64
QEMUOPTS = \
-nographic \
-smp $(CPUS) \
-machine virt \
-bios $(BOOTLOADER) \
-kernel kernel \
+ -drive file=$(U)/fs.img,if=none,format=raw,id=x0 \ # 以 user/fs.img 作为磁盘镜像
+ -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 # 虚拟 virtio 磁盘设备
我们OS的读写文件操作均在内核态进行由于不确定读写磁盘的结束时间这意味着我们需要新的中断方式——外部中断来提醒OS读写结束了。而要在内核态引入中断意味着我们不得不短暂开启在内核态的嵌套中断。一旦OS打开了文件那么我们就可以获得文件对应的fd了(实际上lab6中我们做了类似的事情就可以使用sys_write/sys_read对文件进行读写操作。