操作系统第五次实验

实验简介

本实验要求在模拟的I/O系统之上开发一个简单的文件系统。用户通过create, open, read等命令与文件系统交互。文件系统把磁盘视为顺序编号的逻辑块序列,逻辑块的编号为0至L − 1。I/O系统利用内存中的数组模拟磁盘。

实验背景

  • IO系统
    实际物理磁盘的结构是多维的:有柱面、磁头、扇区等概念。I/O系统的任务是隐藏磁盘的结构细节,把磁盘以逻辑块的面目呈现给文件系统。逻辑块顺序编号,编号取值范围为0至L−1,其中L表示磁盘的存储块总数。实验中,我们可以利用数组ldisk[C][H][B]构建磁盘模型,其中CHB 分别表示柱面号,磁头号和扇区号。每个扇区大小为512字节。I/O系统从文件系统接收命令,根据命令指定的逻辑块号把磁盘块的内容读入命令指定的内存区域,或者把命令指定的内存区域内容写入磁盘块。文件系统和I/O系统之间的接口由如下两个函数定义:
    read_block(int i, char *p);
    该函数把逻辑块i的内容读入到指针p指向的内存位置,拷贝的字符个数为存储块的长度B。
    write block(int i, char *p);
    该函数把指针p指向的内容写入逻辑块i,拷贝的字符个数为存储块的长度B。此外,为了方便测试,我们还需要实现另外两个函数:一个用来把数组ldisk 存储到文件;另一个用来把文件内容恢复到数组。
  • 文件系统
    文件系统位于I/O系统之上。
  • 用户与文件系统之间的接口
    文件系统需提供如下函数;create, destroy, open, read, write。
    create(filename): 根据指定的文件名创建新文件。
    destroy(filename): 删除指定文件。
    open(filename): 打开文件。该函数返回的索引号可用于后续的read, write, lseek,或close操作。
    close(index): 关闭制定文件。
    read(index, mem_area, count): 从指定文件顺序读入count个字节memarea指定的内存位置。读操作从文件的读写指针指示的位置开始。
    write(index, mem_area, count): 把memarea指定的内存位置开始的count个字节顺序写入指定文件。写操作从文件的读写指针指示的位置开始。
    lseek(index, pos): 把文件的读写指针移动到pos指定的位置。pos是一个整数,表示从文件开始位置的偏移量。文件打开时,读写指针自动设置为0。每次读写操作之后,它指向最后被访问的字节的下一个位置。lseek能够在不进行读写操作的情况下改变读写指针能位置。
    directory: 列表显示所有文件及其长度。
  • 文件系统的组织
    磁盘的前k个块是保留区,其中包含如下信息:位图和文件描述符。位图用来描述磁盘块的分配情况。位图中的每一位对应一个逻辑块。创建或者删除文件,以及文件的长度发生变化时,文件系统都需要进行位图操作。前k个块的剩余部分包含一组文件描述符。每个文件描述符包含如下信息:
    • 文件长度,单位字节
    • 文件分配到的磁盘块号数组。该数组的长度是一个系统参数。在实验中我们可以把它设置为一个比较小的数,例如3。

实验过程

主要数据结构
#define B		10			//存储块长度
#define L		500			//存储块总数
#define K		100			//保留区大小
#define BUSY	1
#define FREE	0
#define OK		1
#define ERROR	0
#define FILE_BLOCK_LENGTH		(B-3)				//文件分配磁盘块号数组长度
#define FILE_NAME_LENGTH		(B-1)				//最长文件名长度
#define FILE_SIGN_AREA			((L-1-K)/B+1)		//保留区中文件标识符起始块号位图之后
#define FILE_NUM				FILE_BLOCK_LENGTH	//目录内最多文件数目
#define BUFFER_LENGTH			25					//打开文件表目中的缓冲区长度
#define INPUT_LENGTH			100					//写文件时最大输入长度
#define OUTPUT_LENGTH			100					//读文件时最大读出长度
struct filesign {							//文件描述符
	int file_length;						//文件长度
	int filesign_flag;						//占用标识位
	int file_block;							//文件分配磁盘块号数组实际长度
	int file_block_ary[FILE_BLOCK_LENGTH];	//文件分配磁盘块号数组
};

struct contents {							//目录项
	char filename[FILE_NAME_LENGTH];		//文件名
	int	 filesignnum;						//文件描述符序号
};

struct openfilelist {						//打开文件表表目
	char buffer[BUFFER_LENGTH];				//读写缓冲区
	int pointer[2];							//读写指针文件内容的位置
	int filesignnum;						//文件描述符
	int flag;								//占用符
};

char ldisk[L][B];						//用字符数组模拟磁盘

openfilelist open_list[FILE_NUM];		//打开文件表
主要函数声明
										//辅助函数
int show_openlist();				//显示打开文件表,返回打开文件个数
void directory();					//显示目录文件详细信息
void show_help();					//该文件系统的帮助
void show_ldisk();					//显示磁盘内容(辅助用)
									//核心函数
void read_block(int, char *);		//文件系统与IO设备的接口函数,读取块
void write_block(int, char *);		//文件系统与IO设备的接口函数,写入块
void Init();						//初始化文件系统
int create(char *);					//创建文件
int destroy(char *);				//删除文件
int open(char *);					//打开文件
int close(int);						//关闭文件
int read(int, int, int);				//读文件
int write(int, int, int);				//写文件
int write_buffer(int, int);			//把缓冲区内容写入文件
int lseek(int, int);					//定位文件指针
void Init_block(char, int);			//初始化字符数组块(辅助)
命令菜单

显示所有实现的功能
在这里插入图片描述

目录

我们的文件系统中仅设置一个目录,该目录包含文件系统中的所有文件。除了不需要显示地创建和删除之外,目录在很多方面和普通文件相像。目录对应0号文件描述符。初始状态下,目录中没有文件,所有,目录对应的描述符中记录的长度应为0,而且也没有分配磁盘块。每创建一个文件,目录文件的长度便增加一分。目录文件的内容由一系列的目录项组成,其中每个目录项由如下内容组成:
• 文件名
• 文件描述符序号
主要代码

void directory()
//列表显示所有文件及其长度。
{
	int i;
	int filenum;
	int filelength;
	char filename[FILE_NAME_LENGTH];
	char temp[B];
	char tempd[B];
	char temps[B];
	read_block(FILE_SIGN_AREA, temp);
	filenum = temp[1];						//实际存在的文件个数
	printf("\n");
	if (filenum == 0)
	{
		printf("\t\t\t\t该目录下没有文件\n");
	}

	for (i = 0; i < FILE_NUM; i++)
	{
		read_block(temp[3 + i], tempd);					//读取目录项
		if (tempd[0] != 0)
		{
			read_block(tempd[0] + FILE_SIGN_AREA, temps);		//读取文件描述符
			if (temps[0] == BUSY && tempd[0] != 0)
			{
				filelength = temps[1];
				strcpy(filename, tempd + 1);
				printf("%-10s\t\t%-2d字节\n", filename, filelength);
			}
		}
	}

	if (filenum != 0)
	{
		printf("\t\t\t\t共%d个文件\n", filenum);
	}
}

运行演示结果
在这里插入图片描述

文件的创建与删除

创建文件时需要进行如下操作;
• 找一个空闲文件描述符(扫描ldisk [0]~ldisk [k - 1])
• 在文件目录里为新创建的文件分配一个目录项(可能需要为目录文件分配新的磁盘块)
• 在分配到的目录项里记录文件名及描述符编号.
• 返回状态信息(如有无错误发生等)
删除文件时需要进行如下操作(假设文件没有被打开):
• 在目录里搜索该文件的描述符编号
• 删除该文件对应的目录项并更新位图
• 释放文件描述符
• 返回状态信息
主要代码以及运行截图

  • 文件的创建
int create(char filename[])

{
	int i;
	int frees;
	int	freed;
	int freed2;
	char temps[B];
	char tempc[B];
	char temp[B];
	//**************
	for (i = K; i < K + FILE_NUM; i++)
	{
		read_block((i - K) / B, temp);
		if (temp[(i - K) % B] == BUSY)
		{
			read_block(i, temp);
			if (strncmp(temp + 1, filename, FILE_NAME_LENGTH) == 0)
			{
				printf("该目录已经存在文件名为%s的文件\n", filename);
				return ERROR;
			}
		}
	}
	//*************
	for (i = FILE_SIGN_AREA; i < K; i++)
	{
		read_block(i, temp);
		if (temp[0] == FREE)
		{
			frees = i;
			break;
		}
	}
	if (i == K)
	{
		printf("没有空闲的文件描述符\n");
		return ERROR;
	}
	//*************
	for (i = K; i < K + FILE_NUM; i++)
	{
		read_block((i - K) / B, temp);
		if (temp[(i - K) % B] == FREE)
		{
			freed = i;
			break;
		}
	}
	if (i == K + FILE_NUM)
	{
		printf("文件个数已达上限\n");
		return ERROR;
	}
	//*****************
	for (i = K + FILE_NUM; i < L; i++)
	{
		read_block((i - K) / B, temp);
		if (temp[(i - K) % B] == FREE)
		{
			freed2 = i;
			break;
		}
	}
	if (i == L)
	{
		printf("磁盘已满,分配失败\n");
		return ERROR;
	}


	filesign temp_filesign;
	contents temp_contents;
	//*******
	temp_filesign.filesign_flag = 1;
	temp_filesign.file_length = 0;
	temp_filesign.file_block = 1;


	Init_block(temps, B);
	temps[0] = temp_filesign.filesign_flag;
	temps[1] = temp_filesign.file_length;
	temps[2] = temp_filesign.file_block;
	temps[3] = freed2;
	for (i = 4; i < FILE_BLOCK_LENGTH; i++)
	{
		temps[i] = '\0';
	}
	write_block(frees, temps);

	//**************
	temp_contents.filesignnum = frees - FILE_SIGN_AREA;
	strncpy(temp_contents.filename, filename, FILE_NAME_LENGTH);

	Init_block(tempc, B);
	tempc[0] = temp_contents.filesignnum;
	tempc[1] = '\0';
	strcat(tempc, temp_contents.filename);
	write_block(freed, tempc);

	//****************
	read_block((freed - K) / B, temp);
	temp[(freed - K) % B] = BUSY;
	write_block((freed - K) / B, temp);
	read_block((freed2 - K) / B, temp);
	temp[(freed2 - K) % B] = BUSY;
	write_block((freed2 - K) / B, temp);
	read_block(FILE_SIGN_AREA, temp);
	temp[1]++;
	write_block(FILE_SIGN_AREA, temp);

	return OK;


}

运行截图
在这里插入图片描述

  • 文件的删除
int destroy(char * filename)
{
	int i;
	int dtys;
	int dtyd;
	int use_block;
	int index;
	char temp[B];
	char tempd[B];


	for (i = K; i < K + FILE_NUM; i++)
	{
		read_block((i - K) / B, temp);
		if (temp[(i - K) % B] == BUSY)
		{
			read_block(i, temp);
			if (strncmp(temp + 1, filename, FILE_NAME_LENGTH) == 0)
			{
				dtyd = i;
				dtys = temp[0] + FILE_SIGN_AREA;
				index = temp[0];
				break;
			}
		}
	}
	if (i == K + FILE_NUM)
	{
		printf("没有找到该文件\n");
		return ERROR;
	}

	//**
	int list = -1;
	for (i = 0; i < FILE_NUM; i++)
	{
		if (open_list[i].filesignnum == index)
		{
			list = i;
			break;
		}
	}
	if (open_list[list].flag == BUSY && list != -1)
	{
		printf("该文件已经被打开,需要关闭才能删除\n");
		return ERROR;
	}


	//***********


	//**********
	read_block(dtys, temp);
	use_block = temp[2];
	for (i = 0; i < use_block; i++)
	{
		read_block((temp[i + 3] - K) / B, tempd);
		tempd[(temp[i + 3] - K) % B] = FREE;
		write_block((temp[i + 3] - K) / B, tempd);
	}
	//***********
	Init_block(temp, B);
	write_block(dtys, temp);




	//************
	Init_block(temp, B);
	write_block(dtyd, temp);

	//***************
	read_block((dtyd - K) / B, temp);
	temp[(dtyd - K) % B] = FREE;
	write_block((dtyd - K) / B, temp);
	//**********
	read_block(FILE_SIGN_AREA, temp);
	temp[1]--;
	write_block(FILE_SIGN_AREA, temp);


	return OK;

}

在这里插入图片描述

文件的打开与关闭

文件系统维护一张打开文件表.打开文件表的长度固定,其表目包含如下信息:
• 读写缓冲区
• 读写指针
• 文件描述符号
文件被打开时,便在打开文件表中为其分配一个表目;文件被关闭时,其对应的表目被释放。读写缓冲区的大小等于一个磁盘存储块。打开文件时需要进行的操作如下:
• 搜索目录找到文件对应的描述符编号
• 在打开文件表中分配一个表目
• 在分配到的表目中把读写指针置为0,并记录描述符编号
• 读入文件的第一块到读写缓冲区中
• 返回分配到的表目在打开文件表中的索引号
关闭文件时需要进行的操作如下:
• 把缓冲区的内容写入磁盘
• 释放该文件在打开文件表中对应的表目
• 返回状态信息
主要代码以及运行截图

  • 文件打开
    主要代码
open(char * filename)

{
	int i;
	int opd;
	int ops;
	int list;
	char temp[B];
	int index;
	//************
	for (i = K; i < K + FILE_NUM; i++)
	{
		read_block((i - K) / B, temp);
		if (temp[(i - K) % B] == BUSY)
		{
			read_block(i, temp);
			if (strncmp(temp + 1, filename, FILE_NAME_LENGTH) == 0)
			{
				opd = i;
				ops = temp[0];
				//	printf("opd: %d,ops: %d\n",opd,ops);
				break;
			}
		}
	}
	if (i == K + FILE_NUM)
	{
		printf("没有找到该文件\n");
		return ERROR;
	}

	//***********
	for (i = 0; i < FILE_NUM; i++)
	{
		if (open_list[i].filesignnum == ops && open_list[i].flag == BUSY)
		{
			printf("该文件已经被打开\n");
			return ERROR;
		}
	}

	//**********
	for (i = 0; i < FILE_NUM; i++)
	{
		if (open_list[i].flag != BUSY)
		{
			list = i;
			break;
		}
	}

	//**************

	open_list[list].filesignnum = ops;

	open_list[list].flag = BUSY;

	index = open_list[list].filesignnum;
	lseek(index, 0);

	Init_block(open_list[list].buffer, BUFFER_LENGTH);
	read_block(open_list[list].pointer[0], temp);
	strncpy(open_list[list].buffer, temp, BUFFER_LENGTH);

	return OK;

}

实验截图(bjtu文件已删除打不开)
在这里插入图片描述

  • 文件关闭
    主要代码
int close(int index)
/***************************关闭文件
***************************/
{
	int i;
	int list = -1;
	char temp[B];
	//***************根据index找表目
	for (i = 0; i < FILE_NUM; i++)
	{
		if (open_list[i].filesignnum == index)
		{
			list = i;
			break;
		}
	}
	if (list == -1)					//没找到
	{
		printf("没找到当前索引号文件,操作失败...\n");
		return ERROR;
	}
	if (open_list[list].flag != BUSY)		//输入的index对应文件没被打开
	{
		printf("输入的索引号有误,操作失败...\n");
		return ERROR;
	}

	//****************将缓冲区的内容写入磁盘
	/* //测试用
	for(i = 0 ; i < BUFFER_LENGTH-1; i++ )
	{
	open_list[list].buffer[i] = 5;
	}
	*/

	write_buffer(index, list);			//将当前list打开文件表对应的缓冲区入index索引号的文件

										//****************清楚操释放表目
	Init_block(open_list[list].buffer, BUFFER_LENGTH);				//清除缓冲区
	open_list[list].filesignnum = FREE;								//清除文件描述符
	open_list[list].flag = FREE;									//清除占用标志位
	open_list[list].pointer[0] = NULL;								//清楚指针
	open_list[list].pointer[1] = NULL;
	return OK;
}

实验截图
在这里插入图片描述

读写

创建文件时需要进行如下操作;
• 找一个空闲文件描述符(扫描ldisk [0]~ldisk [k - 1])
• 在文件目录里为新创建的文件分配一个目录项(可能需要为目录文件分配新的磁盘块)
• 在分配到的目录项里记录文件名及描述符编号.
• 返回状态信息(如有无错误发生等)
删除文件时需要进行如下操作(假设文件没有被打开):
• 在目录里搜索该文件的描述符编号
• 删除该文件对应的目录项并更新位图
• 释放文件描述符
• 返回状态信息
主要代码以及运行截图

  • 读文件
    主要代码
int read(int index, int mem_area, int count)
/*
从指定文件顺序读入count 个字
节mem_area 指定的内存位置。读操作从文件的读写指针指示的位置
开始。
*/
{
	int i;
	int list = -1;
	char temp[B];
	//***************根据index找打开文件表表目
	for (i = 0; i < FILE_NUM; i++)
	{
		if (open_list[i].filesignnum == index)
		{
			list = i;
			break;
		}
	}
	if (list == -1)					//没找到
	{
		printf("没找到当前索引号文件,操作失败...\n");
		return ERROR;
	}
	if (open_list[list].flag != BUSY)		//输入的index对应文件没被打开
	{
		printf("输入的索引号有误,操作失败...\n");
		return ERROR;
	}

	//***************根据index找文件描述符
	char temp_output[OUTPUT_LENGTH];
	Init_block(temp_output, OUTPUT_LENGTH);
	char output[OUTPUT_LENGTH];
	Init_block(output, OUTPUT_LENGTH);

	read_block(FILE_SIGN_AREA + index, temp);
	int file_length = temp[1];					//文件长度
	int file_block = temp[2];					//文件实际使用块
	int file_area;

	//**********************拷贝文件内容至temp_output
	for (i = 0; i < file_block - 1; i++)
	{
		read_block(FILE_SIGN_AREA + index, temp);
		read_block(temp[3 + i], temp);
		strncpy(temp_output + i * B, temp, B);
	}
	read_block(FILE_SIGN_AREA + index, temp);
	read_block(temp[3 + i], temp);
	strncpy(temp_output + i * B, temp, B);

	//******************当前文件读写坐标
	int x = open_list[list].pointer[0];
	int y = open_list[list].pointer[1];

	for (i = 0; i < file_block; i++)
	{
		read_block(FILE_SIGN_AREA + index, temp);
		if (temp[3 + i] == x)
		{
			break;
		}
	}
	file_area = i * B + y;							//转换文件内相对位置									

	for (i = 0; i < count; i++)
	{
		output[i + mem_area] = temp_output[i + file_area];
	}

	printf("%s\n", output + mem_area);
	return OK;
}
  • 写文件
    主要代码
int write(int index, int mem_area, int count)
/*
把mem_area 指定的内存位置开
始的ount 个字节顺序写入指定文件写操作从文件的读写指针指示
的位置开始。
*/
{
	int i;
	int list = -1;
	int input_length;
	char temp[B];

	//*************根据index找到文件打开表
	for (i = 0; i < FILE_NUM; i++)
	{
		if (open_list[i].filesignnum == index)
		{
			list = i;
			break;
		}
	}
	if (list == -1)					//没找到
	{
		printf("没找到当前索引号文件,操作失败...\n");
		return ERROR;
	}
	if (open_list[list].flag != BUSY)		//输入的index对应文件没被打开
	{
		printf("输入的索引号有误,操作失败...\n");
		return ERROR;
	}

	char input[INPUT_LENGTH];
	Init_block(input, INPUT_LENGTH);
	i = 0;
	fflush(stdin);
	while (scanf("%c", &input[i]))
	{
		if (input[i] == '\n')											//回车时终止读入
		{
			input[i] = '\0';
			break;
		}
		i++;
	}
	input_length = i;
	//*******************考虑输入串的长度需要写入的长度为input_length - mem_area
	//缓冲区容量可写入输入内容不需要再分配
	if (count <= BUFFER_LENGTH)
	{
		strncat(open_list[list].buffer, input + mem_area, count);			//存入缓冲区
	}
	//大于缓冲区长度分次写入,需要分块写入
	else
	{
		int rest;						//当前缓冲区空闲容量
		for (i = 0; i < BUFFER_LENGTH; i++)
		{
			if (open_list[list].buffer[i] == FREE)
			{
				rest = BUFFER_LENGTH - i;
				break;
			}
		}
		//第一部分,缓冲区有一定容量rest 将缓冲区写入文件,清空缓冲区	
		strncat(open_list[list].buffer + BUFFER_LENGTH - rest, input + mem_area, rest);
		write_buffer(index, list);
		Init_block(open_list[list].buffer, BUFFER_LENGTH);
		//第二部分,循环(input_length - mem_area)/BUFFER_LENGTH 块缓冲区写入文件
		for (i = 0; i < (count / BUFFER_LENGTH) - 1; i++)
		{
			strncpy(open_list[list].buffer, (input + mem_area) + rest + i * BUFFER_LENGTH, BUFFER_LENGTH);
			write_buffer(index, list);
			Init_block(open_list[list].buffer, BUFFER_LENGTH);
		}
		//第三部分,(count%BUFFER_LENGTH)剩余未满一块的写入缓冲区
		Init_block(open_list[list].buffer, BUFFER_LENGTH);
		strncpy(open_list[list].buffer, (input + mem_area) + rest + i * BUFFER_LENGTH, count%BUFFER_LENGTH);
		int buffer_start;
	}
	return OK;
}

实验截图
在这里插入图片描述

实验代码

GitHub地址:https://github.com/notdz56/16281023-/tree/master/操作系统第五次实验代码

实验目的】 1. 了解文件系统的原理; 2. 用高级语言编写和调试一个简单的文件系统,模拟文件管理的工作过程。从而对各种文件操作命令的实质内容和执行过程有比较深入的了解。 【实验准备】 1.文件的逻辑结构  顺序文件  索引文件  索引顺序文件  直接文件和哈希文件 2.外存分配方式  连续分配  链接分配  索引分配 【实验内容】 1. 实验要求 要求设计一个 n个用户的文件系统,每次用户可保存m个文件,用户在一次运行中只能打开一个文件,对文件必须设置保护措施,且至少有Create、delete、open、close、read、write等命令。 2. 实验题目  设计一个10个用户的文件系统,每次用户可保存10个文件,一次运行用户可以打开5个文件。  程序采用二级文件目录(即设置主目录[MFD])和用户文件目录(UED)。另外,为打开文件设置了运行文件目录(AFD)。  为了便于实现,对文件的读写作了简化,在执行读写命令时,只需改读写指针,并不进行实际的读写操作。 因系统小,文件目录的检索使用了简单的线性搜索。文件保护简单使用了三位保护码:允许读写执行、对应位为 1,对应位为0,则表示不允许读写、执行。程序中使用的主要设计结构如下:主文件目录和用户文件目录( MFD、UFD)打开文件目录( AFD)(即运行文件目录)。 M D F 用户名 文件目录指针 用户名 文件目录指针 U F D 文件名 保护码 文件长度 文件名 A F D 打开文件名 打开保护码 读写指针
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值