Status
Done

实验目的

  1. 扩展Nachos的基本文件系统。Nachos的文件系统是一个简单并且能力有限的系统,限制之一就是文件的大小是不可扩展的。通过扩展,使得文件的大小是可变的。在扩展写入文件内容时,一边写入,一边动态调整文件的长度及所占用的数据扇区。
  1. 增加Nachos文件的最后修改时间,并在执行./nachos -D命令时显示。Nachos文件头中存储文件最后修改时间,时间值是从UTC 1970年1月1日00:00:00来的秒数(精确到1秒),占用原来numSectors的存储位置(从磁盘存储空间效率上考虑,文件头中已经有了文件长度字节数,无需再存储文件内容占用的扇区数)。
参阅:
操作系统课程设计 指导教程 -张鸿烈 2012.pdf,4.2节,pp.84-89
man 2 time
man ctime
man 2 stat
code/lab4/n4a、n4b、n4c、n4d
code/lab4/n4areadme.txt、n4breadme.txt、n4creadme.txt、n4dreadme.txt
code/lab4/n4ascreen.txt、n4bscreen.txt、n4cscreen.txt、n4dscreen.txt
注1:仅普通文件的文件头最后修改时间字段有意义,并在执行./nachos -D命令时显示其时间。对其他文件头对象,在执行./nachos -D命令时不显示时间即可。
注2:若Lab4全部完成,演示提交的代码为带有文件最后修改时间的。
注3:对一般的OS,一个100字节的文件,open后lseek到偏移50处,write 10字节,close后,文件长度还是100字节,不会截短到60字节。这在实现Nachos的-hap命令行选项时需要注意。

实验步骤与内容

Nachos文件管理系统

概述

Nachos在其模拟磁盘上实现了文件系统。它包括一般文件系统的所有的特性。但应该强调,Nachos只是提供了一个现代操作系统的实验平台和框架,而目前实现的文件系统比较简单。下面主要介绍其与UNIX操作系统中文件系统的区别。
  1. 在磁盘的组织结构方面
    1. Nachos中文件同样有其inode和一般存储磁盘块,即Nachos物理磁盘的扇区。但是它不像UNIX一样,将文件系统中所有文件的inode存放在一起,即inode区中。每个Nachos文件的inode占用一个单独的扇区,分散在物理磁盘的任何地方,同一般存储扇区用同样的方式进行申请和回收。它没有文件系统管理块,是通过位图(Bitmap)来管理整个磁盘上的空闲块。
      notion image
  1. 在文件系统空闲磁盘数据块和inode块管理方面
    1. Nachos中有一个特殊的文件,即位图文件。该文件存放的是整个文件系统的扇区使用情况的位图。如果一个扇区为空闲,则它在位图文件相应的位为0,否则为1。Nachos中没有专门对inode扇区进行管理。
      当需要申请一个扇区时,根据位图文件寻找一个空闲的扇区,并将其相应的位置为1。当释放一个扇区时,将位图中相应的位置为0。位图文件是一个临界资源,应该互斥访问现有的文件系统没有实现互斥访问,所以每次只允许一个线程访问文件系统。位图文件的inode占据0号扇区。
  1. 在文件系统的目录管理方面
    1. 一般的文件系统都采用树状目录结构,有的UNIX文件系统还有目录之间的勾连,形成图状文件系统结构。Nachos则比较简单,只有一级目录,也就是只有根目录,所有的文件都在根目录下。而且根目录中可以存放的文件数是有限的。Nachos文件系统的根目录同样也是通过文件方式存放的,它的inode占据了1号扇区。
  1. 文件的索引结构上
    1. Nachos同一般的UNIX一样,采用索引表进行物理地址和逻辑地址之间的转换,索引表存放在文件的inode中。但是目前Nachos采用的索引都是直接索引,所以Nachos的最大文件长度不能大于4K。

文件系统的实现

文件系统实现的结构如图所示,核心代码均在filesys文件夹中
notion image
  1. 同步磁盘分析
    1. Nachos模拟的磁盘是异步设备,当发出访问磁盘的请求后立刻返回,直到从磁盘读出或写入数据结束后,发出磁盘中断,说明一次磁盘访问真正结束。
      然而,Nachos是一个多线程的系统,在概述中提到过,有的文件系统没有实现互斥访问,所以为了避免系统混乱,必须对线程访问磁盘作出限制:
      • 同时只能有一个线程访问磁盘
      • 当发出磁盘访问请求后,必须等待访问的真正结束
      与同步磁盘相关代码在文件synchdisk.cc和文件synchdisk.h
      SynchDisk类定义如下
      通过信号量和锁机制实现同步磁盘,以读出磁盘数据为例,当线程向磁盘发出读请求后,首先通过调用lock->Acquire()来获取锁,这样可以保证在任意时间内,只有一个线程能够对磁盘进行I/O操作。接下来发起一个异步读取请求指定扇区的磁盘数据。在读取完成之前,线程调用semaphore->P()方法,使自己阻塞,等待中断信号的到来,这样确保读取过程完成后,该线程才继续执行。最后,当读取操作结束,信号量被释放时,线程释放之前获取的锁,允许其他线程继续进行磁盘I/O操作。
  1. 文件系统模块分析 Nachos文件系统模块的主要类是FileSystem,该类负责管理文件的创建、删除、读取等操作。它通过使用位图来管理磁盘块的分配,通过根目录来管理文件的名称和相关信息。类中最重要的方法包括Create()Open()Remove(),这些方法在设计上类似于Unix操作系统的creatopenunlink系统调用,但没有权限管理的机制。
    1. FileSystem()构造函数
      用于初始化文件系统。当formattrue时,它会格式化磁盘,生成新的位图和空白的根目录;当formatfalse时,它会加载已有的文件系统(位图文件和目录文件)。
      格式化磁盘时,先创建空白位图和根目录,位图用于记录磁盘中各扇区的使用情况;再为文件头分配扇区,包括位图和目录的文件头,将其写回磁盘,以便后续的文件操作。打开位图文件和目录文件,确保在Nachos运行期间保持这些文件打开状态,将空白位图写回磁盘
      Create()方法
      在文件系统创建一个指定大小的文件;首先在根目录中查找是否存在同名文件。如果存在,则返回false。否则开始创建文件,为新文件分配一个扇区来保存其文件头。如果申请不到扇区,则返回false;将新文件的信息添加到根目录中;根据文件的初始大小,分配相应数量的磁盘数据块。如果所有操作成功,将文件头、目录和位图的修改写回磁盘。 Open()方法
      打开一个已有的文件,使其可以被读写
      Remove()方法
      用于删除文件,包括释放文件占用的所有资源,并更新根目录和位图。
  1. 文件头模块分析
    1. Nachos系统文件头相当于UNIX文件系统中的inode结构,它给出了一个文件除了文件名之外的所有属性,包括文件长度、地址索引表等等
      Nachos中的FileHeader模块实现了对文件数据块的管理,包括分配和释放数据块、从磁盘读取和写回文件头等功能。具体来说,FileHeader通过一个固定大小的表来存储指向磁盘扇区的指针,用于定位文件的数据块。由于FileHeader的大小受限于一个磁盘扇区,因此它能管理的最大文件大小也是有限的(约4KB),Nachos文件头的索引表只有直接索引。
      Allocate()
      为新创建的文件分配数据块,并初始化文件头。它根据文件的大小,计算需要的磁盘扇区数量,然后从空闲位图(BitMap)中分配这些数据块。 Deallocate()
      取消分配为此文件的数据块分配的所有空间。依次检查索引表,首先检查某一项所对应扇区是否在位图中置1,freeMap->Clear((int)dataSectors[i])清除分配给该文件的扇区(位图中置0)
      FetchFrom()
      用于将存储在磁盘上的文件头读入内存,从而可以对其进行后续的操作,例如读取文件内容或写入新的数据。 WriteBack()
      在文件头内容被修改后,用于将这些修改持久化到磁盘中,以保证文件系统状态的正确性。
3. 打开文件结构分析
在Nachos操作系统中,OpenFile类用于管理打开的文件,并提供对文件进行读写的接口,类似于UNIX中的文件描述符。OpenFile类的实例保存文件的相关信息,例如文件头(FileHeader)和当前读写位置,支持对文件的基本操作,如打开、关闭、读写文件等。 ReadAt()
从指定位置position开始读取numBytes个字节到into缓冲区中
先检查读取请求的合法性,确保不超过文件长度;然后计算起始和结束扇区,确定需要读取的磁盘扇区数;从磁盘中读取这些扇区到一个内部缓冲区buf,最后将实际需要的数据复制到into缓冲区中。
WriteAt()
从指定位置position开始,将from缓冲区中的numBytes个字节写入文件;
同样需要检查写入请求的合法性、并计算起始与结束扇区,确定需要写入的扇区数;对于首尾不完整的扇区,先从磁盘读取到内部缓冲区,以保留未修改的部分;将from缓冲区的数据写入到内部缓冲区合适的位置,再将修改后的缓冲区内容写回磁盘。
💡
WriteAt()实现了文件的随机写入功能,可以再文件的任意位置进行写操作,而不影响文件的当前读写指针。为了防止覆盖不相关数据,在处理部分扇区时,必须先读取再写入。

扩展文件系统,实现动态调整文件长度

对文件长度相关的操作主要定义在文件头中,即文件头模块分析部分,我们了解到该模块灭有构造函数,取而代之的是Allocate函数,可以通过为文件分配块或从磁盘读取来初始化文件头;所以当写入文件后若写入位置大于目前文件长度,则通过某种手段扩展文件的大小;同时还需要考虑到若扩展后需要分配新的扇区该如何处理。
在FileHeader类中新建一个扩展函数Extend,用于修改文件大小的分配,与写入文件相关的函数在OpenFile类中,分别是Write和WriteAt,此处只需要修改WriteAt的代码,因为Write主要是通过调用WriteAt实现的。只需要修改if语句,原先的代码中,如果写入位置大于文件长度,则视为非法操作,直接返回,而现在只需要在大于时调用Extend函数进行扩展即可
接下来是Extend函数的具体实现,该函数有一个参数newSize,为新的文件的大小,当且仅当该数值大于目前文件的大小时,才需要进行扩展。为确保代码的严谨性,需要进行边界检查,若传入newSize小于或等于已分配的大小时,函数会直接返回;
接下来计算需要重新分配的新扇区数量,此处选择使用divRoundUp函数分别计算当前文件所占扇区数量和新文件所占扇区数量,记为numSectors和newNumSectors;考虑到可能刚好超出少量字节,但若仍需要占据一个扇区的情况,数值需要向上取整。
判断原有扇区是否能容纳新文件的所有字节,否则需要新增扇区。
需要新增的扇区数量为新文件对于扇区数量减去旧文件对于扇区数量,使用OpenFile读入0号扇区的位图,判断新文件对应扇区是否超过了一级索引所能容纳的最大扇区,或者该磁盘上已经没有足够的空闲空间,如果有这两种情况,则返回失败。

增加文件最后修改时间

增加Nachos文件的最后修改时间,并在执行./nachos -D命令时显示。Nachos文件头中存储文件最后修改时间,时间值是从UTC 1970年1月1日00:00:00来的秒数(精确到1秒),占用原来numSectors的存储位置(从磁盘存储空间效率上考虑,文件头中已经有了文件长度字节数,无需再存储文件内容占用的扇区数)。
由于numSectors在文件中有多处使用,所以事先进行更名处理,将FileHeader类中的numSectors变量改为updateTime,并为其增加相应的getter和setter函数
而原先的numSectors的值可以通过文件占用字节数进行计算,因此只需要在原来使用到numSectors的地方之前加上它的定义即可
文件修改的主要操作在fstest.cc中,包括Copy、NAppend、Append等,我们需要根据实际情况来判断是否需要进行对时间戳的修改,下面进行讨论
在Copy方法中,由于目标文件直接复制被复制文件,所以最后修改的时间戳应该与被复制文件相同,所以直接使用stat函数从UNIX文件系统中读取被复制文件的时间戳,并写入目标文件中即可。
在修改过程中注意到,fstest中函数与文件的交互主要是通过OpenFile对象完成,若要修改文件的时间戳,则需要再OpenFile类中将hdr的时间戳暴露出来,所以添加了两个函数,方便对时间戳的读取和修改
Copy方法中新增修改时间戳代码如下
在NAppend方法中,则需要分类讨论了,若追加的内容文件不存在,相当于无需追加内容,那么函数会直接返回;若被读取的文件不存在,则需要创建一个新文件,新文件的时间戳为追加的内容文件的时间戳;若被读取的文件存在且追加内容文件也存在,则相当于对被读取文件进行修改,需要将时间戳更新为当前系统时间。由于NAppend方法所涉及的文件均为Nachos文件,所以可以直接调用openFile中的函数对时间戳进行读取或修改。
新建一个布尔变量needCreate来记录是否需要创建新文件
然后根据needCreate的值,将对应时间戳写回即可,使用time函数可以获得当前系统时间戳
而Append方法中逻辑与NAppend相似,只不过追加的内容文件为UNIX系统中的文件,需要仿照Copy方法使用stat函数获取追加的UNIX系统文件的时间戳(detail中这个方法是复用的,但是sry有一点懒就没拎出来了)

测试

  1. 格式化 Nachos 文件系统
notion image
notion image
  1. 复制大小不同的文件
notion image
notion image
notion image
复制三个文件后,输入./nachos -D查看扇区分配情况
文件内容从 \ff\ff 开始,这表明前 16 个扇区(即 015)已被分配。每个 \ff 表示 8 个比特都为 1可以从输出中看出扇区的分配情况
  • smallNach 文件使用了两个扇区:5 (文件头)和 6。
  • mediumNac 文件使用了三个扇区:7(文件头)、8 和 9(数据块)。
  • bigNachos 文件使用了六个扇区:10(文件头)、11、12、13、14 和 15(数据块)。
  1. 复制 huge 文件到 Nachos 文件系统
  1. smallNachos文件追加到bigNachos
验证文件系统是否能够动态扩展现有文件以容纳更多数据
可以看到big文件分配的扇区多了一个16,且FileContents后面出现了small文件内容,并且文件修改时间也更新为系统当前时间,表明追加文件成功导致文件扩展并使用了更多的扇区,实验成功实现了文件系统的扩展以及最后修改时间的添加。

结论分析与体会

本次实验通过扩展 Nachos 的文件系统,使得文件的大小可以动态调整,并增加了文件的最后修改时间记录功能。这些改进使得 Nachos 的文件系统更接近现实中的操作系统文件系统,同时也让我们对文件系统的实现细节有了更深入的理解。
在扩展文件大小的过程中,我们学会了如何有效管理磁盘扇区,以及如何在不产生大量磁盘碎片的情况下实现文件扩展。在增加文件修改时间的过程中,我们深入理解了时间戳在文件系统中的作用,以及如何有效地进行时间数据的存储和更新。
这一系列扩展工作让我深刻认识到文件系统设计的复杂性和精妙之处。每一个功能的增加都需要仔细考虑存储空间、效率和实现的复杂性,同时还要尽量保持系统的简洁和可维护性。这也是操作系统设计中最具挑战和乐趣的部分。
 
Loading...