问题
如何在根目录中创建文件?
根目录的本质
根目录在文件系统中是一个特殊的文件
根目录中存储了文件的基本信息 (FileEntry)
FileEntry 包含了文件名,文件起始扇区,文件大小等信息
根目录逻辑结构
文件实现的基础:扇区管理
如何 获取 / 归还 空闲扇区?
如何查找当前扇区的后续扇区?
如何为当前文件 增加 / 删除 一个扇区?
扇区申请
一些注意事项
扇区操作是一种外存操作,因此需要仔细计算目标位置
扇区管理时使用相对扇区地址,扇区读写时使用绝对地址
关系如下
- offset = si - mapSize - 2
- si = offset + mapSize + 2
扇区分配表的每个 item 下标和数据区每个扇区的下标是一一对应的 (从0开始)。
访问数据扇区对应的分配表项
计算相对地址:offset = si - mapSize - 2
计算目标扇区:sctOff = offset / MAP_ITEM_CNT
计算扇区内偏移:idxOff = offset % MAP_ITEM_CNT
需要实现的部分扇区管理函数
核心数据结构和数量关系
扇区管理函数的实现
fs.c
typedef struct
{
uint* pSct;
uint sctIdex;
uint sctOff;
uint idxOff;
}MapPos;
MapPos FindInMap(uint si)
{
MapPos ret = {0};
FSHeader* header = (si != SCT_END_FLAG) ? (FSHeader*)ReadSector(HEADER_SCT_IDX) : NULL;
if(header)
{
uint offset = si - FIXED_SCT_IDX - header->mapSize;
uint sctOff = offset / MAP_ITEM_CNT;
uint idxOff = offset % MAP_ITEM_CNT;
uint* ps = (uint*)ReadSector(sctOff + FIXED_SCT_IDX);
if(ps)
{
ret.pSct = ps;
ret.sctIdex = si;
ret.sctOff = sctOff;
ret.idxOff = idxOff;
}
}
Free(header);
return ret;
}
static uint AllocSector()
{
uint ret = SCT_END_FLAG;
FSHeader* header = (FSHeader*)ReadSector(HEADER_SCT_IDX);
if(header && (header->freeBegin != SCT_END_FLAG))
{
MapPos mp = FindInMap(header->freeBegin);
if(mp.pSct)
{
uint* pInt = AddrOff(mp.pSct, mp.idxOff);
uint next = *pInt;
uint flag = 1;
ret = header->freeBegin;
header->freeNum--;
header->freeBegin = next + FIXED_SCT_IDX + header->mapSize;
*pInt = SCT_END_FLAG;
flag = flag && HDRawWrite(HEADER_SCT_IDX, (byte*)header);
flag = flag && HDRawWrite(FIXED_SCT_IDX + mp.sctOff, (byte*)mp.pSct);
if(!flag)
{
ret = SCT_END_FLAG;
}
}
Free(mp.pSec);
}
Free(header);
return ret;
}
static uint FreeSector(uint si)
{
uint ret = 0;
FSHeader* header = (si == SCT_END_FLAG) ? NULL : (FSHeader*)ReadSector(HEADER_SCT_IDX);
if(header)
{
MapPos mp = FindInMap(si);
if(mp.pSct)
{
uint* pInt = AddrOff(mp.pSct, mp.idxOff);
*pInt = header->freeBegin - FIXED_SCT_IDX - header->mapSize;
header->freeNum++;
header->freeBegin = si;
ret = HDRawWrite(HEADER_SCT_IDX, (byte*)header) &&
HDRawWrite(FIXED_SCT_IDX + mp.sctOff, (byte*)mp.pSct);
}
Free(mp.pSct);
}
Free(header);
return ret;
}
static uint NextSector(uint si)
{
uint ret = SCT_END_FLAG;
FSHeader* header = (si == SCT_END_FLAG) ? NULL : (FSHeader*)ReadSector(HEADER_SCT_IDX);
if(header)
{
MapPos mp = FindInMap(si);
if(mp.pSct)
{
uint* pInt = AddrOff(mp.pSct, mp.idxOff);
if(*pInt != SCT_END_FLAG)
{
ret = *pInt + FIXED_SCT_IDX + header->mapSize;
}
}
Free(mp.pSct);
}
Free(header);
return ret;
}
static uint FindLast(uint sctBegin)
{
uint ret = SCT_END_FLAG;
uint next = sctBegin;
while(next != SCT_END_FLAG)
{
ret = next;
next = NextSector(next);
}
return ret;
}
static uint FindPrev(uint sctBegin, uint si)
{
uint ret = SCT_END_FLAG;
uint next = sctBegin;
while((next != SCT_END_FLAG) && (next != si))
{
ret = next;
next = NextSector(next);
}
if(next == SCT_END_FLAG)
{
ret = SCT_END_FLAG;
}
return ret;
}
static uint FindIndex(uint sctBegin, uint idx)
{
uint ret = sctBegin;
uint i = 0;
while((ret != SCT_END_FLAG) && (i < idx))
{
ret = NextSector(ret);
i++;
}
return ret;
}
static uint MarkSector(uint si)
{
uint ret = (si == SCT_END_FLAG) ? 1 : 0;
MapPos mp = FindInMap(si);
if(mp.pSec)
{
uint* pInt = (uint*)AddrOff(mp.pSct, mp.idxOff);
*pInt = SCT_END_FLAG;
ret = HdRawWrite(FIXED_SCT_IDX + mp.sctOff, (byte*)mp.pSct);
}
Free(mp.pSec);
return ret;
}
test
void test_1()
{
uint i = 0;
uint a[5] = {0};
FSHeader* header = (FSHeader*)ReadSector(HEADER_SCT_IDX);
printf("begin alloc, sector num = %d\n", header->freeNum);
Free(header);
for(i = 0; i < 5; i++)
{
a[i] = AllocSector();
}
header = (FSHeader*)ReadSector(HEADER_SCT_IDX);
printf("after alloc, sector num = %d\n", header->freeNum);
Free(header);
for(i = 0; i < 5; i++)
{
printf("a[%d] = %d\n", i, a[i]);
FreeSector(a[i]);
}
header = (FSHeader*)ReadSector(HEADER_SCT_IDX);
printf("after free, sector num = %d\n", header->freeNum);
Free(header);
}
void test_2()
{
FSHeader* header = (FSHeader*)ReadSector(HEADER_SCT_IDX);
uint i = 0;
uint curr = header->freeBegin;
printf("header->freeNum = %d\n", header->freeNum);
while(curr != SCT_END_FLAG)
{
i++;
curr = NextSector(curr);
}
printf("i = %d\n", i);
Free(header);
}
MapPos 结构体中记录了分配表和数据区之间的下标关系。
我们本文件系统的策略是扇区管理时使用相对扇区地址,扇区读写时使用绝对地址。当需要通过数据区扇区的绝对地址找到对应管理扇区的相对地址时,FindInMap 函数返回一个 MapPos 结构体来做这个事情。
分配表类似于链表,分配表上的每一个值都代表了它的下一个扇区号。AllocSector 函数用于分配一个空闲扇区,它需要将 header->freeBegin 对应分配单元的值取出来,保存到 next 中,这个值代表了此扇区的下一个扇区号,接下来将 header->freeBegin 作为分配出来的扇区号进行返回,然后将 header->freeBegin 对应分配单元的值设置为 SCT_END_FLAG,代表这个扇区已被分配,最后将 header->freeBegin 重新赋值为 next + FIXED_SCT_IDX + header->mapSize。还需要把修改的内容写到内存中。FreeSector 函数将对应的扇区号进行回收,首先找到 si 所对应的分配单元,将这个分配单元上的值赋值为 header->freeBegin - FIXED_SCT_IDX - header->mapSize,表示这个扇区号的下一个扇区号为 header->freeBegin,然后将 header->freeBegin 赋值为 si,si 成为了空闲链表的头部。还需要把修改的内容写到内存中。
test_1 测试结果如下
分配5个扇区后,总扇区数减少了5;当回收5个扇区后,总扇区数增加了5。并且第二次获取到的扇区号正好和第一次相反,这是因为最后回收的扇区会被添加到链表的头部,所以首先分配到的扇区号是最后被回收的扇区。 这和我们的预期相同。
test_2 测试结果如下
当前空闲的扇区数为162028,我们通过 NextSector 来遍历空闲链表,遍历了162028次,表明空闲扇区数有162028。这和我们的预期是相同的。