实验五——16281042

文件系统

16281042 李许增

github链接https://github.com/LiXuzeng/2019_OS_LXZ/tree/master/16281042_%E6%9D%8E%E8%AE%B8%E5%A2%9E_%E5%AE%9E%E9%AA%8C%E4%BA%94

实验简介

本实验要求在假设的I/O系统之上开发一个简单的文件系统,这样做既能让实验者对文件系统有整体了解,又避免了涉及过多细节。用户通过create,open,read等命令与文件系统交互。文件系统把磁盘视为顺序编号的逻辑块序列,逻辑块的编号为0至L - 1。I/O系统利用内存中的数组模拟磁盘。

实验内容

整体架构

本次实验使用二维数组模拟硬盘,使用位图、文件描述符、目录来描述文件,打开文件表操作文件。所有定义放在FileSystem.h中,实现及主函数在FileSystem.cpp中,程序使用C语言完成。整体结构层次关系如下图:
在这里插入图片描述

模拟硬盘

以ldisk.txt文件来存储ldisk[L][B]的数据。本实验中设置L和B大小均为10。文件中每一盘块一行,最后附加终止符一行,共11行,每个数据之间空格分隔,共20列,总大小应为243字节。数组中初值为全零,存储在文件中以空格或者字符0来表示。硬盘文件示例如下图:
在这里插入图片描述
硬盘前K个盘块为系统保留盘,本次实验中K = 3。其后的2个盘块用来存储目录文件,剩下的盘块自由存储文件。在系统保留盘中,盘块0存放位图,由于本次实验中硬盘块数和位数均为10,故位图占满0号盘;盘块1~2存放文件描述符,动态存储,尚未占用时为0,后续详细说明。由此可知,上图中硬盘表示的内容如下图所示:
在这里插入图片描述
系统变量定义如下:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// L磁盘的存储块总数
#define L 10
// B表示每个存储块的长度
#define B 10
// K为系统保留盘数大小
#define K 3
//文件描述符数量
#define fdcpsize ((K-1)*B/4)
// 模拟磁盘
char ldisk[L][B];

char tmp[10][20];

位图

位图用来描述磁盘块的分配情况,共10位,0表示未占用,1表示已占用。创建或者删除文件,以及文件的长度发生变化时,文件系统都需要进行位图操作。

//位图,0代表空,1代表被占用
int bmp[L];

文件描述符

前k个块的剩余部分包含一组文件描述符。每个文件描述符包含,文件长度和文件分配到的磁盘块号数组。在实验中我们可以把它设置为一个比较小的数,例如3。磁盘块号数组中,0位表示块号,1位表示位号(偏移),2位为状态位表示是否占用。

//文件描述符
struct Fdcp
{
	int len;//文件长度
	int fpos[3];//磁盘块号数组,0位为块号,1位为偏移,2位为状态,0为空闲1为占用
}fdcp[fdcpsize];

目录

我们的文件系统中仅设置一个目录,该目录包含文件系统中的所有文件。除了不需要显示地创建和删除之外,目录在很多方面和普通文件相像。目录对应0号文件描述符。初始状态下,目录中没有文件,所以,目录对应的描述符中记录的长度应为0。每创建一个文件,目录文件的长度便增加一分。目录文件的内容由一系列的目录项组成,其中每个目录项由文件名和文件描述符序号组成。
本程序中目录设有目录总长代表目录文件大小,目录总数代表目录项个数。目录项中包含文件描述符序号和文件名,为了方便读写和判断,每个目录项以’!’作为结尾。

//目录
struct Index {
	int fdcpnum;//描述符序号
	char* fname;//文件名
	char end = '!';//每个目录尾
};
struct FIndex
{
	int size = 0;//目录总长
	int num = 0;//目录总数
	Index* index;//目录项
}findex;

打开文件表

文件系统维护一张打开文件表.打开文件表的长度固定,其表目包含读写缓冲区、读写指针、文件描述符号。文件被打开时,便在打开文件表中为其分配一个表目;文件被关闭时,其对应的表目被释放。
由于模拟磁盘空间有限,文件个数不会太多,故将打开文件表表目数量设定成为了固定值5。此数值完全是自主设定和方便起见,可以任意改变,也可以设定为oft指针,分配时申请空间关闭时释放。

//打开文件表
struct OFT {
	char* buff; //读写缓冲区
	int rwpos = 0; //读写指针
	int fpos = 0; //文件描述符号
}oft[5];

I/O系统

I/O系统层面包含4个函数,两个读取,两个写入。该系统定义如下:

//I/O系统

// 该函数把逻辑块i的内容读入到指针p指向的内存位置,拷贝的字符个数为存储块的长度B
void read_block(int i, char* p);

// 该函数把指针p指向的内容写入逻辑块i,拷贝的字符个数为存储块的长度B
void write_block(int i, char* p);

// 存储到文件
void write_to_file();

/*把文件内容恢复到数组
 *若文件为空,则返回-1
 *若文件大小不对,返回-2
 *否则返回0
 */

其中,指定逻辑块的读写较为简单,直接将模拟硬盘中的对应字符和指针p对应位置相互赋即可。注意到如果硬盘对应字符是初始值,或者是随机小于0的值,则用字符0来代替赋值。

void write_to_file()

存储到文件。该函数功能是将内存中的位图、目录、文件描述符等系统项存储到ldisk当中,同时将ldisk整体写入ldisk.txt,即生成真实硬盘中的硬盘文件。流程如下:
在这里插入图片描述
写入位图较为简单,直接和ldisk[0]赋值即可。
写入文件描述符,首先写入文件长度len,再写入磁盘块号数组中的各项。由于描述符开始于ldisk[1][0],故可以计算出每个项目该写到磁盘何处,使用指针加偏移的方式写入磁盘。

//写入文件描述符
	for (j = 0; j < fdcpsize; j++) {
		*(ldisk[1] + 4 * j) = fdcp[j].len + '0';
		*(ldisk[1] + 4 * j + 1) = fdcp[j].fpos[0] + '0';
		*(ldisk[1] + 4 * j + 2) = fdcp[j].fpos[1] + '0';
		*(ldisk[1] + 4 * j + 3) = fdcp[j].fpos[2] + '0';
	}

目录从ldisk[3][0]起始,可以仿照文件描述符的写入方式。根据目录总长size,维护一个自ldisk[3][0]算起的偏移指针。先写入描述符序号,再根据文件名长度,写入文件名,最后附加’!’作为每个目录项结尾。如果目录未满,则以’0’填充剩余磁盘位。

//写入目录
	int p = 0;
	for (i = 0; i < findex.num; i++) {
		*(ldisk[3] + p++) = findex.index[i].fdcpnum + '0';
		for (j = 0; j < strlen(findex.index[i].fname); j++, p++)
			*(ldisk[3] + p) = findex.index[i].fname[j];
		*(ldisk[3] + p++) = '!';
	}
	for (; p < 20; p++) {
		*(ldisk[3] + p) = '0';
	}

打开和写入文件使用freopen函数fclose函数实现。最后将输出流交回控制台即可。

int read_from_file()

从ldisk.txt中读取硬盘内容。使用系统提供的fseek函数和ftell函数获取该文件的长度,当不满足上文提到的243个字节大小时判定为硬盘文件损坏,为空则返回-1,有内容但损坏则返回-2。其余交由init函数来进行处理,将在shell部分详细说明。

文件系统

文件系统位于I/O系统之上。文件系统需提供如下函数:create,destroy,open,read,write,lseek,directory。文件系统的定义如下:

// 文件系统

/*根据指定的文件名创建新文件
 *成功,返回0
 *无空闲描述符,返回-1
 *目录分配不成功,返回-2
 *没有空盘符,返回-3
 */
int create(char* filename);

/*删除指定文件
 *删除成功,返回0
 *没有该文件,返回-1
 */
int destroy(char* filename);

/*打开文件。该函数返回的索引号可用于后续的read,write,lseek,或close操作。
 *打开成功,返回0
 *没有该文件,返回-1
 *打开文件已满,返回-2
 */
 int open(char* filename);
 /*关闭指定文件
 *成功关闭,返回0
 *文件未打开,返回-1
 */
int close(int index);

/*从指定文件顺序读入count个字节mem area指定的内存位置。读操作从文件的读写指针指示的位置开始
 *成功读取,返回0
 *文件未打开,返回-1
 *到达文件尾,返回-2
 */
int read(int index, char* mem_area, int count);

/*把mem_area指定的内存位置开始的count个字节顺序写入指定文件。写操作从文件的读写指针指示的位置开始
 *写入成功,返回0
 *文件未打开,返回-1
 *count过大导致覆盖后续文件,返回-2
 */
int write(int index, char* mem_area, int count);
/*把文件的读写指针移动到pos指定的位置。pos是一个整数,表示从文件开始位置的偏移量。
 *文件打开时,读写指针自动设置为0。每次读写操作之后,它指向最后被访问的字节的下一个位置。
 *lseek能够在不进行读写操作的情况下改变读写指针能位置。
 *成功移动,返回0
 *文件未打开,返回-1
 *移动超过文件尾,返回-2
 */
int lseek(int index, int pos);

//列表显示所有文件及其长度
int directory();

int create(char* filename)

创建文件。找一个空闲文件描述符扫描ldisk[0]~ldisk[k-1];在文件目录里为新创建的文件分配一个目录项;在分配到的目录项里记录文件名及描述符编号。无空闲描述符,返回-1,目录分配不成功,返回-2,没有空盘符,返回-3。
根据fdcpsize顺序扫描文件描述符,当发现某一个描述符块号数组中状态位为0,即空闲时,将其分配给此文件。

//寻找空描述符
	for (i = 1; i < fdcpsize; i++) {
		if (fdcp[i].fpos[2] == 0) {
			startpos = i;
			fdcp[i].fpos[2] = 1;
			break;
		}
	}
	//如无空描述符,返回-1
	if (startpos == 0)
		return -1;

因为已经给目录留好了空位,对于文件名大小设有限制(详见实验反思),故有空闲描述符即代表有空闲目录。只需将文件描述符、文件名等信息写入下一个目录项即可。将目录对应的文件描述符状态位置一,以免这是第一个用户文件时使用了目录文件的描述符。

//分配目录项
	//如目录分配不成功,返回-2
	findex.index = (Index*)realloc(findex.index, (findex.num + 1) * sizeof(Index));
	if (findex.index == NULL)
		return -2;
	else findex.num++;
	//创建新目录
	fdcp[0].fpos[2] = 1;
	findex.index[findex.num - 1].fdcpnum = startpos;
	findex.size += strlen(filename) + 2;
	fdcp[0].len = findex.size;
	findex.index[findex.num - 1].fname = (char*)malloc(strlen(filename) * sizeof(char));
	strcpy(findex.index[findex.num - 1].fname, filename);

根据位图寻找空闲盘符分配磁盘空间,如无空盘符则返回-3,交由shell处理。此部分放在写入目录之前,故如没有空盘符目录中也不会生成对应目录项。

//分配空间
	int have = 0;
	for (i = 0; i < L; i++) {
		//扫描位图,寻找空盘符
		if (bmp[i] == 0) {
			fdcp[startpos].fpos[0] = i;
			fdcp[startpos].fpos[1] = 0;
			bmp[i] = 1;
			have = 1;
			break;
		}
	}

int destroy(char* filename)

删除文件。在目录里搜索该文件的描述符编号,删除该文件对应的目录项并更新位图,释放文件描述符,没有该文件,则返回-1,成功删除则返回0。
扫描目录,使用strcmp函数来寻找文件名对应的文件。如果未找到则返回-1。找到后提取文件描述符序号。
根据其文件描述符,对应位图置空,对应磁盘处置零,释放文件描述符(状态位置零)。

//删除位图、文件、文件描述符
	bmp[fdcp[fdcpnum].fpos[0]] = 0;
	for (i = 0; i < fdcp[fdcpnum].len; i++) {
		ldisk[fdcp[fdcpnum].fpos[0]][fdcp[fdcpnum].fpos[1] + i] = '0';
	}
	fdcp[fdcpnum].len = 0;
	for (i = 0; i < 3; i++)
		fdcp[fdcpnum].fpos[i] = 0;

根据文件名,计算需要删除的目录长度,删除对应目录项,更新目录总长,将其后的目录项前移。如果此时没有目录项,则将目录的文件描述符一同释放。

//删除目录
	int deslen = 0;//需要删除的目录长度
	findex.index[indexnum].fdcpnum = 0;
	deslen = strlen(findex.index[indexnum].fname);
	findex.index[indexnum].fname = NULL;
	findex.size -= deslen + 2;
	fdcp[0].len = findex.size;
	//后续目录项前移
	for (i = indexnum; i < findex.num; i++) {
		findex.index[i] = findex.index[i + 1];
	}
	findex.num--;
	if (findex.num == 0)
		fdcp[0].fpos[2] = 0;

int open(char* filename)

读写缓冲区的大小等于一个磁盘存储块。打开文件时需要进行的操作如下:搜索目录找到文件对应的描述符编号;在打开文件表中分配一个表目;在分配到的表目中把读写指针置为0,并记录描述符编号;读入文件的第一块到读写缓冲区中;返回分配到的表目在打开文件表中的索引号
同destroy函数一样,根据文件名在目录中搜索对应文件,如未找到则返回-1。
维护打开文件表。在分配表目时,同时判断已经打开的表目中是否含有该文件,如果已经打开,则返回-2,如果表目已满,则返回-3。但已经打开优先于表目已满。

//没有该文件,返回-1
	if (status == 0)
		return -1;
	for (i = 0; i < 5; i++) {
		//文件已经打开,返回-2
		if (oft[i].fpos == fdcpnum) {
			return -2;
		}
		if (oft[i].fpos == 0) {
			oftnum = i;
			break;
		}
	}
	//打开文件已满,返回-3
	if (oftnum == -1)
		return -3;

成功分配表目以后,则给buff分配空间,读写指针置零,读入对应第一块磁盘数据。返回表目序号。

oft[oftnum].buff = (char*)malloc(B * sizeof(char));
	oft[oftnum].fpos = fdcpnum;
	oft[oftnum].rwpos = 0;
	//读入文件盘块
	read_block(fdcp[fdcpnum].fpos[0], oft[oftnum].buff);
	return oftnum;

int close(int index)

关闭文件时需要进行的操作如下:把缓冲区的内容写入磁盘;释放该文件在打开文件表中对应的表目;返回状态信息。
如果对应表目的描述符为0,则表示没有此打开的文件,返回-1。
如果buff区非空,则调用write_block函数将buff区写入磁盘,之后释放表目,将一切置空。

//该文件未打开,返回-1
	if (oft[index].fpos == 0)
		return -1;
	//缓冲区写入文件
	if (oft[index].buff != NULL) {
		int fpos = fdcp[oft[index].fpos].fpos[0];
		write_block(fpos, oft[index].buff);
	}
	//释放表目
	oft[index].fpos = 0;
	oft[index].rwpos = 0;
	oft[index].buff = NULL;

int read(int index, char* mem_area, int count)

同close相同,如果未打开则返回-1。
如果buff区为空,则分配B大小的空间。此操作用来防止刚进行过write操作,将buff区写入后置空,导致read_block报错的问题。
设置int型变量ds:sp表示文件读写位置,ds为文件所在盘号,sp为对应盘号的偏移。其中sp的数值为文件描述符盘块数组中的偏移,加上打开文件表中读写指针。
在不超过文件总长的情况下,按磁盘块读取文件。此时维护两个指针:mem_area中的下一个字符位置i,以及sp。当sp超过盘块大小时,读取下一个盘块,并将sp -= B。不断增加i的数值,直到达到count或者超过文件总长停止。最后更新文件读写指针。如果超过文件总长时也未达到count,则返回-2,交由shell处理。

//该文件未打开,返回-1
	if (oft[index].fpos == 0)
		return -1;
	int i = 0;
	//根据文件描述符找到文件位置
	int ds = fdcp[oft[index].fpos].fpos[0];//磁盘块号
	int sp = fdcp[oft[index].fpos].fpos[1] + oft[index].rwpos;//磁盘偏移
	int filelen = fdcp[oft[index].fpos].len;//文件长度
	if (oft[index].buff == NULL)
		oft[index].buff = (char*)malloc(B * sizeof(char));
	while (i < count) {
		read_block(ds++, oft[index].buff);
		for (; i < count && i + sp < B && i + oft[index].rwpos < filelen; i++)
			mem_area[i] = oft[index].buff[i + sp];
		//读够count,返回0
		if (i == count)
			break;
		//到达文件尾,返回-2
		if (i + oft[index].rwpos == filelen) {
			mem_area[i] = '\0';
			oft[index].rwpos = filelen;
			return -2;
		}
		sp -= B;
	}
	oft[index].rwpos += i;
	mem_area[i] = '\0';

int write(int index, char* mem_area) /(int index, char* mem_area, int count)

写操作的初始设置同读操作相同,文件未打开则返回-1,同样使用ds:sp来写入文件。函数流程如下:
在这里插入图片描述
不同的是文件长度获取到初值后,不作为终止的判断依据。当写满当前buff后,将目前的buff写入盘块,如果为写满count,则判断下一个盘块是否被占用,如果占用返回-2,否则继续写入,直到写满count个字符。

while (i < count) {
		read_block(ds++, oft[index].buff);
		for (; i < count && sp < B; i++, sp++) {
			oft[index].buff[sp] = mem_area[i];
			oft[index].rwpos++;
		}
		write_block(ds - 1, oft[index].buff);
		bmp[ds - 1] = 1;
		//写够count长度,返回0
		if (i == count)
			break;
		//count过大,覆盖下一个文件,返回-2
		int a = ds - fdcp[oft[index].fpos].fpos[0];
		if (bmp[ds] != 0 && filelen - (a * B) <= 0) {
			fdcp[oft[index].fpos].len = oft[index].rwpos;
			oft[index].buff = NULL;
			return -2;
		}
		//尚未写满count,重新读入下一块磁盘
		sp -= B;

最后清空buff,如果读写指针已经大于之前记录的文件长度,则将文件长度更新为读写指针的大小。

int lseek(int index, int pos)

更改读写指针。文件未打开则返回-1。同上述函数相同,设置好sp后,判断是否超过文件尾,是则返回-2,读写指针不移动,否则将读写指针移动到pos位置,返回0。

//该文件未打开,返回-1
	if (oft[index].fpos == 0)
		return -1;
	//根据文件描述符找到文件位置
	int sp = fdcp[oft[index].fpos].fpos[1] + oft[index].rwpos;//磁盘偏移
	//pos超过文件大小,返回-2
	if (pos >= fdcp[oft[index].fpos].len)
		return -2;
	oft[index].rwpos = pos;

int directory()

显示文件目录,包含目录序号,文件名,文件大小信息。

int directory() {
	int i = 0, j = 0;
	printf("\n文件列表 16281042\n序号\t文件名\t文件长\n");
	for (i = 0; i < findex.num; i++) {
		printf("%d\t%s\t", i + 1, findex.index[i].fname);
		printf("%d\n", fdcp[findex.index[i].fdcpnum].len);
	}
	return 0;
}

Shell

操纵文件系统的外壳程序或者一个菜单驱动系统。
系统定义如下:

//shell

//对内存、目录、描述符初始化
void init();

/*执行指令
 *正常执行,返回0
 *退出,返回-1
 *错误指令,返回-2
 */
int exec(char str[]);

//菜单
void menu();

void init()

调用I/O系统中的read_from_file()函数,根据其返回值进行操作。如果返回-1或-2,表示磁盘文件错误,将一切系统置空。如果返回0,说明磁盘文件正常,进行初始化。
位图和文件描述符的初始化较为简单,此两项长度和位置固定,直接从相应的磁盘位置读入即可。

//初始化位图
		for (i = 0; i < B; i++) {
			bmp[i] = (int)ldisk[0][i] - '0';
		}
		//初始化文件描述符
		for (i = 0; i < fdcpsize; i++) {
			fdcp[i].len = (int)(*(ldisk[1] + i * 4) - '0');
			for (int j = 0; j < 3; j++) {
				fdcp[i].fpos[j] = (int) * (ldisk[1] + j + 1 + i * 4) - '0';
			}
		}

目录的初始化较为复杂。首先根据fdcp[0]获取目录总长size,维护一个int型变量count,表示当前已经读取的目录长度。
随后初始化目录项。给目录项分配空间,同时将目录数量num加一。维护另一个int型变量num,表示此目录项的长度,当对此目录项操作完毕后,count += num。维护int型变量p,表示目前正在读取的位置。
如果num == 0,表示正在读取新的目录项,则p位置的数据为当前目录项文件描述符序号,否则为文件名。使用字符型数组tmp暂存当前目录项的文件名。当p位置为’!’时表示目录项读取完毕,将tmp最后添加’\0’,赋值给fname。
不断执行上段所述流程,知道count与size相等,此时完成目录初始化。
具体代码实现如下:

//初始化目录
int p = 0, count = 0;
findex.size = fdcp[0].len;
for (i = 0; count < fdcp[0].len - 1; i++) {
	int num = 0;
	j = 0;
	findex.index = (Index*)realloc(findex.index, ++findex.num * sizeof(Index));
	while (*(ldisk[3] + p) != '!') {
		if (num == 0) {
			findex.index[i].fdcpnum = *(ldisk[3] + p) - '0';
		}
		else
		{
			tmp[i][j] = *(ldisk[3] + p);
			j++;
		}
		p++;
		num++;
	}
	tmp[i][j] = '\0';
	findex.index[i].fname = tmp[i];
	p++; num++;
	count += num;
}

int exec(char str[])

分析str,进行分词,执行指令。分词算法与Unix Shell实验中分词算法相同。创建字符数组com[4][30]作为Unix Shell实验中的*args[]。当遇到str中空格时表示已经进入下一个指令参数,使用下一个字符数组存储。实现代码:

while (i < len) {
		for (j = 0; str[i] != ' ' && str[i] != '\0'; i++, j++) {
			com[k][j] = str[i];
		}
		i++;
		com[k][j] = '\0';
		k++;
	}

根据com[0]中字符串的不同,执行不同指令。指令内容包含所有文件系统中描述过的函数。对于不同的指令,有不同的函数调用和异常值处理。以create指令为例,其处理代码如下:

if (strcmp(com[0], "create") == 0) {
		switch (create(com[1])) {
		case 0:strcpy(rtn, com[1]); strcat(rtn, "创建成功"); break;
		case -1:strcpy(rtn, "无空闲描述符"); break;
		case -2:strcpy(rtn, "目录分配不成功"); break;
		case -3:strcpy(rtn, "没有空盘符"); break;
		default:strcpy(rtn, "未知情况"); break;
		}
}

其中rtn为字符型数组,用来存储提示信息。例如创建文件成功后,提示信息为对应文件名+创建成功。
如果com[0]中内容不包含任何能够调用的函数,则提示“指令不支持”,返回-2;如果指令内容为exit,则返回-1。此处的返回值都交由menu处理。

void menu()

系统菜单。显示支持的指令格式,输入指令提示,个人信息。调用exec(str)函数,当其返回-1时,返回主函数,写入磁盘文件,程序结束。

void menu() {
	char com[50];
	int status = 1;
	printf("操作系统实验 文件系统 16281042\n");
	printf("create filename\ndestroy filename\nopen filename\nclose index\nread index count\nwrite index string count / write index string\nlseek index pos\ndirectory\n");
	printf("请输入指令,以空格分隔\n");
	while (status >= 0) {
		fflush(stdin);
		printf("osh>");
		gets_s(com);
		status = exec(com);
	}
}

实验结果

当磁盘文件为空时

启动程序:
在这里插入图片描述
正确显示硬盘文件状态,显示指令格式及个人信息。
由于磁盘为空,应当创建文件。输入create abc
在这里插入图片描述
系统提示该文件创建成功。
打开该文件,写入内容:helloabc
在这里插入图片描述
系统提示操作成功
读取该文件的内容
在这里插入图片描述
正确提示了错误信息。由于刚进行完写操作,读写指针在文件尾部,故应当先移动读写指针到文件头。再进行读操作。
在这里插入图片描述
刚才对于abc文件写入了“helloabc”,将读写指针移动到0,即文件头,读取后续5个字符,即为“hello”,读取正确。
在不关闭当前文件的情况下,新建另一文件nihao
在这里插入图片描述
提示创建成功。
打开此文件,写入“nihao16281042”
在这里插入图片描述
统提示写入成功。此字符串显然大于磁盘位数10,读取文件验证写入成功。同样先移动读写指针到文件头。
在这里插入图片描述
可以看到读取内容正确。
显示文件目录
在这里插入图片描述
可以看到文件名称及长度均正确。
由于系统中优先将最近的空闲盘号分配给新创建的文件,故nihao必然在abc的紧接着的后一个磁盘块中。在abc中写入超过10字符的信息以验证系统正确性。
在这里插入图片描述
系统提示“将覆盖下个文件”,提示正确。此时移动读写指针到文件头,查看文件内容。
在这里插入图片描述
由于刚才写入时读写指针在当时的文件尾,故刚才写入是在abc后续添加。所以显示“hellohello”为正确值。显示目录,文件长正确。
输入exit,生成ldisk.txt文件,查看磁盘中信息。
程序正常退出。
打开磁盘文件。内容如下:
在这里插入图片描述
首行为位图,剩余最后两块空盘,与实际磁盘情况符合;23行为文件描述符,<对应ASCII码60,与数字0相差12,目录长度正确,盘块数组为301,盘块3偏移0处开始,占用状态为1占用,目录文件描述正确;后续文件描述符:与0相差10,=与0相差13,均与实际文件长符合;45为目录,共两项,文件描述符分别为1、2,文件名正确,以!结尾,目录正确;后续文件中,文件内容与测试时相符。
至此,空盘情况下测试完毕。

当磁盘文件正常时

完成上面空盘测试后,已经生成了正确的磁盘文件,此时再次运行程序,验证磁盘读取。
在这里插入图片描述
先后查看目录,打开文件abc,读取超过文件大小的文件内容,提示信息正确,显示内容与磁盘中内容相符。
打开文件nihao,测试文件关闭。分别关闭文件abc,nihao,和一个不存在的打开文件索引。
在这里插入图片描述
前两个文件关闭成功,后一个文件提示未打开。提示正确。
退出,查看磁盘文件
此时磁盘文件与上次文件相同。
至此,磁盘文件正常的测试完毕。

实验反思

  1. 限于磁盘大小,原则上将文件名限制在5个字符以内。但对于文件名大小没有附加判断,可以超过此限制,后果是可能出现有空文件描述符但目录文件已经写满的情况。
  2. 文件目录的初始化使用了很多的指针和提示性的变量,应该可以后续优化。
  3. 由于指令分词时以空格作为判断符,故此方法写入文件时不能有空格。但磁盘读取时以scanf(“%c ”….)的方法读入,故文件中可以出现空格。限于时间关系,没有单独写使用字符串变量调用write的方法,而是直接将输入的指令直接写入,但后续可以实现。

实验总结

文件系统实验十分复杂,我用了相当长的时间来自主完成此实验,让我对文件系统的实现和操作逻辑有了很深入的理解。
由于此实验相当复杂,希望能够在以后的课程实验设计中给此实验分配更长的时间,或者继续简化内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值