BBS水木清华站∶精华区

发信人: raner (lilo), 信区: Linux 
标  题: NACHOS论坛(3) 
发信站: BBS 水木调试站 (Thu Jun  4 16:47:19 1998) 
 
作  家: solmon (所罗门王) on board 'Unix' 
题  目: NACHOS论坛(3) 
来  源:  鼓浪听涛站 
日  期: Thu Mar  6 23:04:30 1997 
出  处: [email protected] 
 
          第三章    文件系统 
 
 
    Nachos文件系统的界面类似于UNIX,有与UNIX的creat,open,close,read,write, 
lseek和unlink相似(不是完全一样)的系统调用.一个重要的不同点在于Nachos文件系 
统是用C++实现的.Creat(相当于UNIX中的creat),Open(open),和Remove(unlink)都是 
定义在FileSystem类中的,因为它们都是与正在操作的文件名和目录相联系的. 
FileSystem::Open返回一个指针,它指向一个OpenFile对象,OpenFile对象与UNIX中的 
打开文件描述符类似,可以用这个对象直接对文件进行操作,如Seek(lseek), Read 
(read), Write(write)只要把这个OpenFile对象删除(delete)就可以关闭(close)这个 
已打开文件了. 
    Nachos文件系统的结构如图: 
 
      ┏━━━━━━━━━━━━━━━━━━━┓ 
      ┃           文  件  用  户             ┃ 
      ┣━━━━━━━━━━━━━━━━━━━┫ 
      ┃ FileSystem   OpenFile     Directory  ┃ 
      ┣━━━━━━━━━━━━━━━━━━━┫ 
      ┃             FileHeader               ┃ 
      ┣━━━━━━━━━━━━━━━━━━━┫ 
      ┃              SynchDisk               ┃ 
      ┣━━━━━━━━━━━━━━━━━━━┫ 
      ┃               Disk                   ┃ 
      ┗━━━━━━━━━━━━━━━━━━━┛ 
 
 
    在Nachos文件系统中,许多数据结构既可存放在内存里,又可存放在磁盘里.为了一 
致起见,文件系统中Synchdisk以上的类都有一个FetchFrom成员函数,它把数据结构从 
磁盘读到内存;还有一个WriteBack成员函数,它与FetchFrom相反,把数据结构从内存写 
回磁盘.在内存中的数据结构与磁盘上的完全一致,没有增减数据项,给管理带来了不少 
方便.在以后的介绍中这两个函数不再重复了. 
    让我们从下往上分析文件系统.Disk模拟了一个物理磁盘,它的具体实现在机器模 
拟部分已经介绍了. 
 
    SynchDisk类在物理磁盘的基础上定义了一个同步磁盘的抽象界面.象别的I/O设备 
一样,物理磁盘是异步设备.用户提出读写请求后立即返回,这以后会有一个中断发生, 
报告磁盘操作完成.同步磁盘有些不同,进程提出读写请求后将睡眠等待,以后在磁盘� 
断的处理函数中唤醒它,所以进程将等待磁盘操作完成后才能返回. 
    SynchDisk类的界面为: 
              ┏━━━━━━━━━━━� 
              ┃      SynchDisk       ┃ 
              ┣━━━━━━━━━━━┫ 
              ┃     ReadSector       ┃ 
              ┃     WriteSector      ┃ 
              ┗━━━━━━━━━━━┛ 
    SynchDisk类的生成函数生成了一个锁,一个信号量,一个物理磁盘对象,由于物理 
磁盘一次只能接受一个读写请求,锁就是用来实现读写互斥的.信号量用来实现中断程 
序和发出磁盘请求的程序之间同步。 
    ReadSector成员函数的执行过程如下: 
          1.关闭锁.(如果别的进程正在操作磁盘,则睡眠等待别的进程的磁盘操作� 
            成) 
          2.向物理磁盘发读数据请求. 
          3.对信号量作P操作(睡眠等待磁盘操作的完成,将由读磁盘的中断处理程序 
            唤醒本进程). 
          4.解锁. 
    WriteSector成员函数的执行过程与ReadSector类似. 
 
    FileHeader类是用来管理一个文件块在磁盘上分布情况的数据结构.这个数据结构 
称为文件控制块.文件控制块中还可以包括一些有关文件的信息,如长度,拥有者等等, 
文件控制块与UNIX中的i-node类似.文件控制块可以放在内存中或磁盘里.当它在磁盘 
里时,只占用一个扇区,Nachos没有实现文件控制块的间接定位,所以文件控制块的大小 
是固定的,也导致Nachos文件最大长度为4Kbytes. 
    FileHeader的界面为: 
              ┏━━━━━━━━┓ 
              ┃  FileHeader    ┃ 
              ┣━━━━━━━━┫ 
              ┃  Allocate      ┃ 
              ┃  Deallocate    ┃ 
              ┃  ByteToSector  ┃ 
              ┃  FileLength    ┃ 
              ┣━━━━━━━━┫ 
              ┃  numBytes      ┃ 
              ┃  numSectors    ┃ 
              ┃  dataSectors[] ┃ 
              ┗━━━━━━━━┛ 
    其中numBytes 为文件长度,numSectors为文件占用的扇区数,dataSectors[]数组 
指明文件的每个数据块存放在第几个扇区里.例如,有一个文件长度为200 byte,Nachos 
定义一个扇区的长度为128 byte,则numBytes=200, numSectors= ceil(200/128)=2,文 
件占用了两个扇区,如果文件的第一块放在第5扇区,第二块放在第20扇区,则 
dataSectors[0]=5,dataSectors[1]=20,dataSectors数组其他元素值不定,也没有用. 
    Allocate成员函数用来初始化文件控制块,为本文件分配磁盘空间.它的执行过程如 
下: 
          1.根据文件长度对numBytes,numSectors赋初值. 
          2.如果文件需要的扇区数大于磁盘上的空闲扇区数,则返回失败. 
          3.给文件分配空闲扇区,把扇区号放入dataSectors数组,并把这些扇区的使 
                  用标志从空闲改为已占用. 
    Deallocate成员函数释放文件占用的扇区. 
    ByteToSector返回存放着文件第x字节数据的扇区号. 
    FileLength返回文件长度. 
    OpenFile类用来实现文件的读写操作.Nachos将文件的操作转化为对扇区的操作. 
在现有实现中并没有考虑文件系统的并发问题,这些留给学生作为课程作业.OpenFile 
的界面为: 
 
              ┏━━━━━━━━┓ 
              ┃    OpenFile    ┃ 
              ┣━━━━━━━━┫ 
              ┃    Seek        ┃ 
              ┃    ReadAt      ┃ 
              ┃    WriteAt     ┃ 
              ┃    Read        ┃ 
              ┃    Write       ┃ 
              ┃    Length      ┃ 
              ┣━━━━━━━━┫ 
              ┃  seekPosition  ┃ 
              ┃ FileHeader* hdr┃ 
              ┗━━━━━━━━┛ 
    seekPosition是文件当前读写指针,hdr是文件控制块的地址.OpenFile的生成函数 
带有一个int型的参数,指明存放文件控制块的扇区号.生成函数先给hdr分配空间,然后 
从磁盘中读出这个文件的文件控制块,放入hdr指向的内存.seekPositon 赋为0,使得文 
件当前读写指针指向文件开头. 
    Seek成员函数将文件当前读写指针移到新的位置. 
 
    ReadAt/WriteAt用于读写数据.读写的开始位置和读写数据长度由参数指定.由于 
Nachos文件长度不可改变,所以超过文件长度的读写都被忽略.又因为磁盘读写单位是 
整个扇区,所以ReadAt读出包括要读数据的所有扇区,然后只把要求读出的数据拷贝给 
调用者.WriteAt先读出写入数据所在的全部扇区,然后把新数据写入要修改的位置,最 
后将这些扇区写回磁盘,这样就保证了不会覆盖不应被修改的部分.这两个函数返回实 
际读/写的长度. 
    Read/Write成员函数分别调用ReadAt/WriteAt成员函数,从文件当前读写指针处开 
始读/写数据.它的执行过程如下: 
          1.分别调用ReadAt/WriteAt函数从文件当前读写指针处开始读/写数据. 
          2.文件当前读写指针向后移动实际读/写的长度. 
          3.返回实际读/写的长度. 
    Length返回文件长度. 
 
    Nachos的目录是由一组目录项组成,每个目录项代表一个文件.目录项的内容有:已 
使用标志inUse,文件控制块所在扇区号sector,文件名字name,文件名有最大长度,这样 
每个目录项都有固定长度.当我们知道文件所在目录和文件名后,就可以从它所在目录 
中找到名字与之相同的目录项,于是就可以知道此文件的文件控制块所在扇区,而文件 
控制块中记录了文件内容所在的扇区号,这样就可以读写这个文件了.与文件系统的其 
他数据结构一样,目录结构既可以放在内存中,又可以放在磁盘上.当它存放在磁盘上时, 
是作为一个常规的Nachos文件存储的,由于Nachos文件长度固定,所以目录的目录项个 
数在生成目录时就固定了,以后不能改变.现有实现中,对目录操作的互斥是由调用者保 
证的. 
    目录的界面为: 
              ┏━━━━━━━━┓ 
              ┃   Directory    ┃ 
              ┣━━━━━━━━┫ 
              ┃   Find         ┃ 
              ┃   Add          ┃ 
              ┃   Remove       ┃ 
              ┃   List         ┃ 
              ┃   FindIndex    ┃ 
              ┣━━━━━━━━┫ 
              ┃   tableSize    ┃ 
              ┃   table        ┃ 
              ┗━━━━━━━━┛ 
    tableSize放的是目录项的个数,table是目录项数组的地址. 
    Directory类的生成函数的执行过程如下: 
          1.此目录的目录项个数由参数传入,用这个参数给 
            tableSize赋值. 
          2.给目录项数组table分配空间,它的元素个数为tableSize. 
          3.将所有目录项的使用标志清零. 
    一个目录类对象才生成时,目录项内容总是空的,如果这个目录已经存在,就需要用 
FetchFrom成员函数把目录项内容从磁盘上读出来. 
    FindIndex(char* name)成员函数在目录中寻找文件名为name的文件,如果找到就 
返回此文件在目录项数组中的索引,否则返回-1. 
    Fine(char* name)成员函数在目录中寻找文件名为name的文件,如果找到就返回此 
文件的文件控制块所在扇区号,否则返回-1. 
    bool Add( char* name,int newSector) 函数增加一个文件到目录中,这个文件的 
名字为name,文件控制块所在扇区号为newSector.如果添加成功,返回TRUE.在下列情况 
返回FALSE: 
          1.目录中已经存在这个文件名. 
          2.目录已满. 
    bool Remove( char* name ) 把名字为name的文件从目录中删除.如果成功返回 
TRUE.若在目录中没有这个文件,则失败,返回FALSE. 
    List() 显示目录中所有的文件. 
    FileSystem是Nachos的文件系统的顶层界面.它提供了一些用文件名来操作文件的 
功能. 
    FileSystem的界面为: 
 
              ┏━━━━━━━━┓ 
              ┃  FileSystem    ┃ 
              ┣━━━━━━━━┫ 
              ┃    Creat       ┃ 
              ┃    Open        ┃ 
              ┃    Remove      ┃ 
              ┃    List        ┃ 
              ┣━━━━━━━━┫ 
              ┃  freeMapfile   ┃ 
              ┃ directoryFile  ┃ 
              ┗━━━━━━━━┛ 
 
    其中freeMapfile和directoryFile是两个OpenFile类的指针. 
    freeMapfile指向用来管理磁盘空间的BitMap文件.磁盘的每个扇区对应BitMap中 
的一位,如果在BitMap中这位为0,则说明此扇区为空,否则说明此扇区已被某个文件占 
用.directoryFile指向根目录文件.Nachos没有实现多级目录结构,它只有一个根目录, 
文件系统的所有文件都放在根目录下. 
    FileSystem的生成函数有一个format参数,如果它为非零,将格式化(Format)整个 
文件系统,否则从模拟磁盘上读出文件系统的信息. 
    Format文件系统的过程为: 
          1.生成一个空的BitMap和根目录. 
          2.把磁盘0扇区分配给freeMapfile文件的文件控制块; 
                  1扇区分配给根目录的文件控制块. 
          3.为这两个文件分配磁盘空间. 
          4.打开这两个文件,即在内存中生成两个OpenFile对象. 
          5.把freeMapfile,directoryFile两个文件写回磁盘. 
          6.释放函数中分配的辅助内存空间,但freeMapfile和directoryFile这两个 
                  OpenFile对象仍然保留在内存中. 
    如果生成文件系统时不需要格式化,就只要从磁盘上读出freeMapfile, 
directoryFile两个文件即可. 
    文件系统生成后,freeMapfile,directoryFile这两个OpenFile对象总是保留在内 
存中的. 
 
    Creat(char* name, int initialSize)成员函数用来创建一个新文件,新文件的名 
字是name,长度为initialSize,它的执行过程如下: 
              ┏━━━━━━━━┓ 
              ┃  从磁盘读入根  ┃ 
              ┃    目录文件    ┃ 
              ┗━━━┳━━━━┛ 
                      ┃ 
                      ↓ 
               ━━━━━━━━━ 
                   目录中已经           Y 
                  存在同名文件       ━━━━━━┓ 
               ━━━━━━━━━                ┃ 
                      ┃                         ┃ 
                      ┃ N                       ┃ 
                      ↓                         ┃ 
              ┏━━━━━━━━┓               ┃ 
              ┃从磁盘读入BitMap┃               ┃ 
              ┃文件,给文件控制 ┃               ┃ 
              ┃块分配一个扇区  ┃               ┃ 
              ┗━━━┳━━━━┛               ┃ 
                      ┃                         ┃ 
                      ┃                         ┃ 
                      ↓                         ┃ 
                 ━━━━━━━        N         ┃ 
                   分配成功      ━━━━━━━→┃ 
                 ━━━━━━━                  ┃ 
                      ┃                         ┃ 
                      ┃ Y                       ┃ 
                      ↓                         ┃ 
              ┏━━━━━━━━┓               ┃ 
              ┃ 将文件加入根   ┃               ┃ 
              ┃     目录       ┃               ┃ 
              ┗━━━┳━━━━┛               ┃ 
                      ┃                         ┃ 
                      ↓                         ┃ 
                 ━━━━━━          N         ┃ 
                   成功加入      ━━━━━━━→┃ 
                 ━━━━━━                    ┃ 
                      ┃                         ┃ 
                      ┃ Y                       ┃ 
                      ↓                         ┃ 
              ┏━━━━━━━━┓               ┃ 
              ┃   为文件分配   ┃               ┃ 
              ┃   磁盘空间     ┃               ┃ 
              ┗━━━┳━━━━┛               ┃ 
                      ┃                         ┃ 
                      ↓                         ┃ 
                 ━━━━━━         N          ┃ 
                   分配成功      ━━━━━━━→┃ 
                 ━━━━━━                    ┃ 
                      ┃                         ┃ 
                      ┃  Y                      ┃ 
                      ↓                         ┃ 
              ┏━━━━━━━━┓               ┃ 
              ┃ 将BitMap文件和 ┃               ┃ 
              ┃ 根目录文件写入 ┃               ┃ 
              ┃    磁盘        ┃               ┃ 
              ┗━━━┳━━━━┛               ┃ 
                      ┃                         ┃ 
                      ┃                         ┃ 
                      ↓                         ↓ 
              ┏━━━━━━━━┓       ┏━━━━━━━━┓ 
              ┃   返回TRUE     ┃       ┃   返回FALSE    ┃ 
              ┗━━━━━━━━┛       ┗━━━━━━━━┛ 
 
    在Creat函数的处理过程中只修改内存中的BitMap和根目录,磁盘上的数据不会被 
修改,这样如果文件生成失败,只要返回FALSE即可.如果执行成功,才把这两个文件写 
回磁盘. 
    Remove(char* name)删除文件名为name的文件.它的执行过程如下: 
          1.从磁盘读入根目录文件. 
          2.如果根目录中没有这个文件,则返回FALSE. 
          3.读入此文件的文件控制块. 
          4.释放此文件占用的磁盘扇区,并删除其目录项. 
          5.释放此文件的文件控制块占用的磁盘扇区. 
          6.把修改后的BitMap和目录文件写回磁盘. 
          7.返回TRUE. 
    OpenFile* Open(char* name)成员函数打开名为name的文件,可以用返回的 
OpenFile指针读写此文件.Open的执行过程如下: 
          1.将目录文件读入内存. 
          2.用Directory::Find(char* name)函数求出name文件的文件控制块所在扇 
                 区. 
          3.读入文件控制块,并生成此文件的OpenFile对象. 
          4.返回OpenFile对象的指针. 
    List成员函数显示根目录的内容. 
    关闭文件只须删除(delete)OpenFile对象即可. 
 
 
-- 
m※ 来源:.鼓浪听涛站 bbs.xmu.edu.cn.[FROM: [email protected]] m 
 
-- 
※ 来源:·BBS 水木调试站 Leeward.lib.tsinghua.edu.cn·[FROM: 166.111.68.98] 

BBS水木清华站∶精华区