Lab8 Report
===========

## 实验目的

通过完成本次实验，希望能达到以下目标

+ 了解基本的文件系统系统调用的实现方法；
+ 了解一个基于索引节点组织方式的`Simple FS`文件系统的设计与实现；
+ 了解文件系统抽象层`VFS`的设计与实现。

## 实验内容

实验七完成了在内核中的同步互斥实验。本次实验涉及的是文件系统，通过分析了解`ucore`文件系统的总体架构设计，完善读写文件操作，重新实现基于文件系统的执行程序机制（即改写`do_execve`），从而可以完成执行存储在磁盘上的文件和实现文件读写等功能。

## 练习

对实验报告的要求：

+ 基于markdown格式来完成，以文本方式为主
+ 填写各个基本练习中要求完成的报告内容
+ 完成实验后，请分析`ucore lab`中提供的参考答案，并请在实验报告中说明你的实现与参考答案的区别
+ 列出你认为本实验中重要的知识点，以及与对应的OS原理中的知识点，并简要说明你对二者的含义，关系，差异等方面的理解（也可能出现实验中的知识点没有对应的原理知识点）
+ 列出你认为OS原理中很重要，但在实验中没有对应上的知识点

## 文件系统的层次架构

`lab8`的关键在于理解操作系统的文件系统层次架构，在`ucore`中，文件系统被划分成了`通用文件系统访问接口`，`文件系统抽象层`(VFS, Virtual File System)，`简单文件系统`(SFS, Simple File System)以及`文件系统I/O设备接口层`四个层次。

+ `通用文件系统访问接口`主要是面向进程，为进程提供方便简单的文件操作系统调用，如我们熟悉的`open`, `read`，`write`等，它通过访问用户系统调用库来获得内核文件系统的相关服务。该层次包括的文件有`user/ulib.c(h)`，`user/syscall.c(h)`，`kern/sysfile.c(h)`。

+ `文件系统抽象层`完成两个方面的工作，一方面为上一层的通用访问接口提供服务，另一方面需要定义和下一层交互的接口。`VFS`层又可以划分为若干个子层，包括处于上层的`file`层，以及处于下层的`inode`层和`fs`层。

	- `file`是面向进程的对磁盘数据块的一种抽象，`file层`主要是提供文件相关的系统调用的实际处理函数，如`file_open`，`file_read`等，另一方面还需要对进程的`打开文件表`进行管理。`file`层对应的文件有`fs/file.c(h)`。

	- `fs`层是对文件系统的抽象，它提供一般的（抽象的）文件系统本身应该完成的一些功能，比如打开文件或者创建文件与目录这种。需要注意的是，文件的读与写是`inode`层提供的功能。`fs`层对应的文件有`vfs/vfs.c(h)`以及`vfsdec.c`，`vfsfile.c`，`vfslookup.c`，`vfspath.c`。

	- `inode`是对文件的一种低层次抽象，和`file`不同，`inode`主要关心的问题是面向底层的操作，如屏蔽不同文件系统之间的细节，而为`file`层提供统一的调用接口。为此，`inode`层以`抽象类`的形式，定义了具体文件系统需要实现的一般操作接口`struct inode_ops`。`inode`层对应的文件有`inode.c(h)`

+ `SFS`层是`ucore`实现的一个具体的文件系统。一方面它需要为`文件系统抽象层`提供服务，即实现`inode`定义的若干抽象操作接口；另一方面则调用底层的设备提供的功能。在`SFS`层中，对文件和目录进行了区分，分别实现了两套不同的抽象接口，即`sfs_node_dirops`和`sfs_node_fileops`。`SFS`层对应的文件包括`fs/sfs`文件夹里面的全部文件。

+ `I/O设备接口层`实际上又可以划分为`抽象设备接口`和`具体设备`两个层次，前者屏蔽了不同设备之间的细节，为上层的文件系统提供统一的接口，后者则关注具体设备，如`disk`，`stdin`，`stdout`，读写等功能的具体实现。该层次对应的文件有`dev.c(h)`以及`dev_disk0.c`等具体设备文件。

在实际的运行过程当中，如用户程序请求读某一个文件，则首先调用用户库中读文件相关的函数，在`ucore`中是`user/file.c::read`，通过层层调用关系，该函数最终通过系统调用进入内核，调用了`sysfile_read`函数。

`sysfile_read`函数调用`vfs`层中`file.c(h)`提供的接口`file_read`来完成实质性的工作。而`file_read`则是调用`inode`层次中的`vop_read`函数进一步将工作向下传递。

`vop_read`调用某个具体文件系统实现的读文件的操作，这里是`sfs_read`函数，随后`sfs_read`调用与`设备接口层`之间的接口`sfs_rblock`与`sfs_rbuf`函数，将工作传递给实现该文件系统的设备层。

在`抽象设备接口层`调用`dop_io`来完成对抽象设备的读操作，该函数调用底层的具体设备，如`disk`，实现的磁盘读写函数，最终完成了对磁盘某几个区块的读操作。

## `file_open`

`file_open`的调用路线

+ `syscall/sys_open`调用`sysfile_open`，转到了`vfs`层的`file_open`；
+ 为了打开一个文件，首先需要找到该文件所在的目录（根目录或者相对路径目录）对应的`inode`，然后再通过读取该`inode`的内容（即`sfs_disk_entry`)，逐个判断路径名是否匹配。
+ 需要注意的是`打开文件`并不类似于`读文件`或者`写文件`这种操作，前者是文件系统应该提供的操作，而后者是某个文件应该提供的接口。因此在`vfs`层中还抽象出了三个子层（也许还有更多），分别是`file`层，`inode`层和`fs`层，`file_open`调用`fs`层的`vfs_open`来完成实际的工作。
+ `vfs_open`调用`vfs_lookup`来进行对路径的查找。
+ `vfs_lookup`首先需要找到根目录或者当前目录的`inode`，因此调用了`get_device`找到起始的查找目录。此后的操作就归结于目录`inode`的查找工作了，因此调用`inode`子层的`vop_lookup`从获得的目录开始查找指定路径。
+ `vop_lookup`调用具体文件系统`sfs`的`sfs_lookup`，后者再调用`sfs_lookup_once`，这表明了`ucore`中实现的文件系统只有一级目录。
+ `sfs_lookup_once`调用`sfs_dirent_search_nolock`逐个读出目录`inode`中记载的内容，即各个文件的`sfs_disk_entry`信息，并对文件名进行比对。为了简单起见，这里的存储策略是每个目录项单独存储在一个`block`当中（这也太浪费了
+ 如果路径正确，`sfs_lookup_once`将找到目标文件的`sfs_disk_entry`，其中记录了它的控制块`sfs_disk_inode`在硬盘上的位置，将该控制块从硬盘读出，并且逐步封装成`sfs_inode`和`inode`，就基本完成了读文件的操作。需要注意的是这里需要调用`sfs`层与`device`层之间的接口，相关函数定义在了`sfs_io.c`当中。
+ 最后在`file_open`函数中，返回打开文件的文件描述符`fd, file descriptor`即可，这里的`fd`实际上是打开文件在进程的`打开文件表`中的索引。
