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[NumDirect+1]; // 最后一项存储二级索引块地址 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 i=numSectors; i<numSectorsSet; i++) dataSectors[i] = freeMap->Find(); } else { // 需要二级索引 if(numSectors < NumDirect) { // 先填满一级索引 for(int i=numSectors; i<NumDirect; 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 i=0; i<numSectorsSet-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 nFiles=0, nBytesInFiles=0, nSectorsOfFiles=0, nFragSectors=0; for(int i=0; i<NumDirEntries; 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文件系统获得了动态扩展能力,同时提供了更完善的磁盘状态监控功能。这种改造方式既保持了原有架构的简洁性,又扩展了系统的实用性。