Files
912-notes/thu_os/lab8_report.md
2019-12-04 20:13:45 +08:00

6.9 KiB
Raw Blame History

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, readwrite等,它通过访问用户系统调用库来获得内核文件系统的相关服务。该层次包括的文件有user/ulib.c(h)user/syscall.c(h)kern/sysfile.c(h)

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

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

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

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

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

  • I/O设备接口层实际上又可以划分为抽象设备接口具体设备两个层次,前者屏蔽了不同设备之间的细节,为上层的文件系统提供统一的接口,后者则关注具体设备,如diskstdinstdout,读写等功能的具体实现。该层次对应的文件有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_rblocksfs_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调用具体文件系统sfssfs_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_inodeinode,就基本完成了读文件的操作。需要注意的是这里需要调用sfs层与device层之间的接口,相关函数定义在了sfs_io.c当中。
  • 最后在file_open函数中,返回打开文件的文件描述符fd, file descriptor即可,这里的fd实际上是打开文件在进程的打开文件表中的索引。