Nachos 文件系统实验四+五:FileHeader 二级索引与 -DI 指令实现 3 大核心修改
Nachos 文件系统实验二级索引与磁盘状态查询的深度实现在操作系统课程设计中Nachos作为一个经典的教学实验平台其文件系统模块的实现一直是学生深入理解存储管理原理的重要实践。本文将聚焦实验四和实验五中关于文件系统核心数据结构FileHeader的改造特别是二级索引的实现和磁盘状态查询指令-DI的添加从技术实现角度提供可落地的解决方案。1. FileHeader 结构改造与二级索引设计传统的Nachos文件系统采用直接索引方式管理文件数据块这限制了文件的最大尺寸。我们需要通过二级索引扩展文件容量同时保持系统的兼容性。1.1 数据结构关键修改在filehdr.h中新增以下字段定义#define NumDirect 10 // 一级索引项数 #define NumDirect2 32 // 二级索引项数 #define MaxFileSectors (NumDirect NumDirect2) // 最大支持扇区数 class FileHeader { private: int dataSectors[NumDirect1]; // 最后一项存储二级索引块地址 unsigned lastModTime; // 最后修改时间戳 bool bHdrChange; // 脏位标记 int sectorNo; // 头部所在扇区号 };核心改造点说明dataSectors数组扩容1项用于存储二级索引块地址新增lastModTime记录文件修改时间引入脏位机制减少磁盘写入1.2 空间扩展函数实现以下是关键的ExtendSpace函数实现展示如何处理二级索引分配bool FileHeader::ExtendSpace(int newFileSize) { int numSectorsSet divRoundUp(newFileSize, SectorSize); if(numSectorsSet numSectors) return true; BitMap *freeMap new BitMap(NumSectors); OpenFile *bitMapFile new OpenFile(0); freeMap-FetchFrom(bitMapFile); // 空间不足检查 if(numSectorsSet MaxFileSectors || freeMap-NumClear() numSectorsSet-numSectors) { delete bitMapFile; delete freeMap; return false; } if(numSectorsSet NumDirect) { // 仅需一级索引 for(int inumSectors; inumSectorsSet; i) dataSectors[i] freeMap-Find(); } else { // 需要二级索引 if(numSectors NumDirect) { // 先填满一级索引 for(int inumSectors; iNumDirect; i) dataSectors[i] freeMap-Find(); numSectors NumDirect; } // 处理二级索引块 int dataSectors2[NumDirect2]; if(dataSectors[NumDirect] -1) { // 首次使用二级索引 dataSectors[NumDirect] freeMap-Find(); memset(dataSectors2, -1, sizeof(dataSectors2)); } else { synchDisk-ReadSector(dataSectors[NumDirect], (char *)dataSectors2); } // 分配二级索引项 for(int i0; inumSectorsSet-numSectors; i) dataSectors2[i] freeMap-Find(); synchDisk-WriteSector(dataSectors[NumDirect], (char *)dataSectors2); } freeMap-WriteBack(bitMapFile); numBytes newFileSize; numSectors numSectorsSet; bHdrChange true; delete bitMapFile; delete freeMap; return true; }关键提示二级索引块本身也占用一个扇区需要在位图中标记。写入操作后必须立即更新磁盘上的位图信息。2. 文件修改时间追踪实现为支持文件修改时间记录需要在关键操作点添加时间戳更新// 在filehdr.h中添加时间接口 time_t GetModTime() { return (time_t)lastModTime; } void SetModTime(time_t modTime) { lastModTime (unsigned)modTime; bHdrChange true; } // 在OpenFile类的读写操作中更新时间 int OpenFile::WriteAt(char *from, int numBytes, int position) { // ...原有写入逻辑... hdr-SetModTime((unsigned)std::time(nullptr)); return numBytes; }时间戳存储采用Unix时间戳格式32位无符号整数在文件头回写磁盘时自动持久化。3. 磁盘状态查询指令实现-DI指令需要统计磁盘的整体使用情况包括统计项说明总扇区数NumSectors已使用扇区位图中已标记的扇区文件占用扇区所有文件实际使用的扇区数内部碎片文件末扇区的未使用空间实现代码如下void FileSystem::PrintInfo() { BitMap *freeMap new BitMap(NumSectors); Directory *directory new Directory(NumDirEntries); FileHeader *fileHdr new FileHeader(); freeMap-FetchFrom(freeMapFile); directory-FetchFrom(directoryFile); // 基础统计 printf(\n磁盘尺寸%d扇区 (%d字节)\n, NumSectors, NumSectors*SectorSize); printf(已使用扇区%d (%.1f%%)\n, NumSectors-freeMap-NumClear(), 100*(NumSectors-freeMap-NumClear())/(float)NumSectors); // 文件详细统计 int nFiles0, nBytesInFiles0, nSectorsOfFiles0, nFragSectors0; for(int i0; iNumDirEntries; i) { int sector directory-GetHdrSecByIndex(i); if(sector ! -1) { nFiles; fileHdr-FetchFrom(sector); int bytes fileHdr-FileLength(); nBytesInFiles bytes; int sectors divRoundUp(bytes, SectorSize); nSectorsOfFiles sectors; if(bytes % SectorSize) nFragSectors; } } printf(\n文件统计\n); printf( 文件数量%d\n, nFiles); printf( 总文件大小%d字节 (占用%d扇区)\n, nBytesInFiles, nSectorsOfFiles); printf( 内部碎片%d字节 (分布在%d个扇区)\n, nSectorsOfFiles*SectorSize - nBytesInFiles, nFragSectors); delete freeMap; delete directory; delete fileHdr; }4. 实验验证与调试技巧验证二级索引是否生效的测试方法创建测试文件并逐步扩展./nachos -cp test/big big ./nachos -ap test/big big # 多次执行直至触发二级索引检查文件头信息./nachos -D关键调试技巧在FileHeader::Print()中添加索引类型输出使用hexdump -C DISK查看磁盘实际布局添加调试输出检查扇区分配顺序常见问题解决方案文件头大小超过128字节调整NumDirect和NumDirect2的比值二级索引块未正确初始化确保新分配的索引块全部初始化为-1时间戳不更新检查bHdrChange标记是否在修改后设置为true通过以上实现Nachos文件系统获得了动态扩展能力同时提供了更完善的磁盘状态监控功能。这种改造方式既保持了原有架构的简洁性又扩展了系统的实用性。