实验内容
一、程序主要功能
1、在内存中开辟一块空间来模拟文件系统的运行,不读写硬盘。
2、面向单用户、单任务,不考虑并发,不考虑文件属主、组等概念。
3、程序开始后,初始化并接收用户输入。若输入”enter”,则重新建立文件系统, 读取上次的退出状态,以上次目录为当前目录; 若输入”q”则退出程序。
系统采用命令行方式与用户交互:touch, rm, ls, mkdir, rmdir, cd ;显示每条命令的执行结果;然后准备接收下一条命令。
用户输入”exit”后,保存当前状态,退出系统 ,等待用户输入。
二、程序主要数据结构
1、文件的属性:
- 文件名(最大长度?) ;
- 文件类型:目录、数据;不考虑链接。
- 文件权限:w/r/x ;
- 文件大小:字节为单位。
- 文件最后修改时间:取程序运行来的时钟滴答数(转换为float)。
2、文件系统的组织:模仿ext2文件系统。
- 超级块:结构体;
- 组描述:结构体;
- 块位图:一维数组;
- inode位图:一维数组;
- inode table::二维数组,每行a~m位表示**,n~x位表示**… ;
- 数据块:每个数据块的头2个字节作指针,指向同一文件的下一数据块。
3、文件的结构:
- 数据文件:第一个数据块的地址在inode table 表项中,后续数据块通过指针连接起来;
- 目录文件:同上。每2个字节用来记录一个子inode。
- 存储空间组织:内存空间分成若干小块。
4、关于touch:
- touch filename:若filename不存在,则创建新数据文件,分配inode,不分配数据块;若filename存在,则修改文件时间。
- touch size filename: 若filename不存在,则创建新数据文件,分配inode,根据size分配数据块; 若filename存在,则修改文件大小和时间。
5、其他命令均简单实现:rm删除一个文件,cd 改变工作目录,mkdir创建一个目录文件,rmdir删除一个空目录。所有命令都要有输出(结果或信息)。
三、程序主要算法
1、存储空间分配算法。
2、回收。
3、命令的实现。
四、程序实现
该程序实现了一个简化的文件系统模拟,包括文件和目录的创建、删除、查看和切换。程序使用了一个名为FileSystem的结构体来表示文件系统,其中包含了块位图、inode位图、inode表和当前目录的inode索引。
代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define MAX_NAME_LENGTH 255
#define MAX_BLOCKS 1024
#define MAX_INODES 1024
#define MAX_DATA_BLOCKS 1024
#define SAVE_FILE "filesystem_state.bin"
typedef struct {
char name[MAX_NAME_LENGTH];
enum {DIRECTORY, DATA} type;
int permissions; // 使用位操作表示权限,例如:0110 (rw-)
int size;
float last_modified_time;
} FileAttributes;
typedef struct {
FileAttributes attributes;
int inode_index;
int data_block_indices[MAX_DATA_BLOCKS];
} File;
typedef struct {
FileAttributes attributes;
int data_block_indices[MAX_DATA_BLOCKS];
} InodeTableEntry;
typedef struct {
int block_bitmap[MAX_BLOCKS];
int inode_bitmap[MAX_INODES];
InodeTableEntry inode_table[MAX_INODES];
int current_directory_inode;
} FileSystem;
float get_current_time() {
return (float)time(NULL);
}
int find_inode(FileSystem *fs, const char *filename) {
for (int i = 0; i < MAX_INODES; i++) {
if (fs->inode_bitmap[i] && strcmp(fs->inode_table[i].attributes.name, filename) == 0) {
return i;
}
}
return -1;
}
int allocate_inode(FileSystem *fs) {
for (int i = 0; i < MAX_INODES; i++) {
if (!fs->inode_bitmap[i]) {
fs->inode_bitmap[i] = 1;
return i;
}
}
return -1;
}
void touch(FileSystem *fs, const char *filename, int size) {
int inode_index = find_inode(fs, filename);
if (inode_index == -1) {
// 文件不存在,创建新文件
inode_index = allocate_inode(fs);
if (inode_index == -1) {
printf("无法分配inode\n");
return;
}
fs->inode_table[inode_index].attributes.type = DATA;
strncpy(fs->inode_table[inode_index].attributes.name, filename, MAX_NAME_LENGTH);
}
// 更新文件属性
fs->inode_table[inode_index].attributes.size = size;
fs->inode_table[inode_index].attributes.last_modified_time = get_current_time();
printf("文件已更新\n");
}
void rm(FileSystem *fs, const char *filename) {
int inode_index = find_inode(fs, filename);
if (inode_index == -1) {
printf("文件不存在\n");
return;
}
if (fs->inode_table[inode_index].attributes.type == DIRECTORY) {
printf("不能删除目录,请使用rmdir命令\n");
return;
}
fs->inode_bitmap[inode_index] = 0;
printf("文件已删除\n");
}
void ls(FileSystem *fs) {
printf("文件名\t类型\t大小\t修改时间\n");
for (int i = 0; i < MAX_INODES; i++) {
if (fs->inode_bitmap[i]) {
FileAttributes attr = fs->inode_table[i].attributes;
printf("%s\t%s\t%d\t%.0f\n", attr.name, attr.type == DIRECTORY ? "目录" : "文件", attr.size, attr.last_modified_time);
}
}
}
void mkdir(FileSystem *fs, const char *dirname) {
int inode_index = find_inode(fs, dirname);
if (inode_index != -1) {
printf("目录已存在\n");
return;
}
inode_index = allocate_inode(fs);
if (inode_index == -1) {
printf("无法分配inode\n");
return;
}
fs->inode_table[inode_index].attributes.type = DIRECTORY;
strncpy(fs->inode_table[inode_index].attributes.name, dirname, MAX_NAME_LENGTH);
printf("目录已创建\n");
}
void rmdir(FileSystem *fs, const char *dirname) {
int inode_index = find_inode(fs, dirname);
if (inode_index == -1) {
printf("目录不存在\n");
return;
}
if (fs->inode_table[inode_index].attributes.type != DIRECTORY) {
printf("不能删除文件,请使用rm命令\n");
return;
}
fs->inode_bitmap[inode_index] = 0;
printf("目录已删除\n");
}
void cd(FileSystem *fs, const char *dirname) {
int inode_index = find_inode(fs, dirname);
if (inode_index == -1) {
printf("目录不存在\n");
return;
}
if (fs->inode_table[inode_index].attributes.type != DIRECTORY) {
printf("不能切换到文件,请输入目录名\n");
return;
}
fs->current_directory_inode = inode_index;
printf("已切换到目录:%s\n", dirname);
}
void save_filesystem_state(FileSystem *fs) {
FILE *file = fopen(SAVE_FILE, "wb");
if (file == NULL) {
printf("无法保存文件系统状态\n");
return;
}
fwrite(fs, sizeof(FileSystem), 1, file);
fclose(file);
printf("文件系统状态已保存\n");
}
void load_filesystem_state(FileSystem *fs) {
FILE *file = fopen(SAVE_FILE, "rb");
if (file == NULL) {
printf("无法加载文件系统状态\n");
return;
}
fread(fs, sizeof(FileSystem), 1, file);
fclose(file);
printf("文件系统状态已加载\n");
}
int main() {
FileSystem fs;
memset(&fs, 0, sizeof(fs));
fs.current_directory_inode = 0; // 初始化当前目录为根目录
printf("输入 \"enter\" 以重新建立文件系统并读取上次的退出状态,输入 \"q\" 退出程序。\n");
char initial_command[10];
fgets(initial_command, sizeof(initial_command), stdin);
initial_command[strcspn(initial_command, "\n")] = 0;
if (strcmp(initial_command, "enter") == 0) {
load_filesystem_state(&fs);
} else if (strcmp(initial_command, "q") == 0) {
return 0;
} else {
printf("未知命令,将使用默认文件系统状态\n");
}
char command[256];
while (1) {
printf("> ");
fgets(command, sizeof(command), stdin);
command[strcspn(command, "\n")] = 0; // 去除换行符
if (strcmp(command, "q") == 0) {
save_filesystem_state(&fs);
break;
} else if (strncmp(command, "touch ", 6) == 0) {
char filename[MAX_NAME_LENGTH];
int size;
sscanf(command + 6, "%s %d", filename, &size);
touch(&fs, filename, size);
} else if (strncmp(command, "rm ", 3) == 0) {
char filename[MAX_NAME_LENGTH];
sscanf(command + 3, "%s", filename);
rm(&fs, filename);
} else if (strcmp(command, "ls") == 0) {
ls(&fs);
} else if (strncmp(command, "mkdir ", 6) == 0) {
char dirname[MAX_NAME_LENGTH];
sscanf(command + 6, "%s", dirname);
mkdir(&fs, dirname);
} else if (strncmp(command, "rmdir ", 6) == 0) {
char dirname[MAX_NAME_LENGTH];
sscanf(command + 6, "%s", dirname);
rmdir(&fs, dirname);
} else if (strncmp(command, "cd ", 3) == 0) {
char dirname[MAX_NAME_LENGTH];
sscanf(command + 3, "%s", dirname);
cd(&fs, dirname);
} else {
printf("未知命令\n");
}
}
return 0;
}
结果:
说明:
1、结构体和类型定义
- FileAttributes:表示文件或目录的属性,包括名称、类型(文件或目录)、权限、大小和最后修改时间。 - File:表示一个文件,包括文件属性、inode索引和数据块索引。 - InodeTableEntry:表示inode表中的一个条目,包括文件属性和数据块索引。 - FileSystem:表示整个文件系统,包括块位图、inode位图、inode表和当前目录的inode索引。
2、辅助函数
- get_current_time():获取当前时间,用于设置文件的最后修改时间。 - find_inode():在文件系统中查找具有给定文件名的inode索引。 - allocate_inode():在文件系统中分配一个新的inode,并返回其索引。
3、文件和目录操作函数
- touch():创建或更新一个文件。如果文件不存在,将创建一个新文件;如果文件已存在,将更新其大小和最后修改时间。 - rm():删除一个文件。如果给定的名称是目录,将提示用户使用rmdir命令。 - ls():列出文件系统中的所有文件和目录。 - mkdir():创建一个新目录。 - rmdir():删除一个目录。如果给定的名称是文件,将提示用户使用rm命令。 - cd():切换到指定的目录。
4、文件系统状态保存和加载函数
- save_filesystem_state():将当前文件系统状态保存到一个二进制文件中。 - load_filesystem_state():从二进制文件中加载文件系统状态。
5、主函数
主函数首先初始化一个FileSystem结构体,并提示用户输入"enter"以重新建立文件系统并读取上次的退出状态,或输入"q"退出程序。然后,程序进入一个循环,等待用户输入命令。用户可以输入touch、rm、ls、mkdir、rmdir和cd命令来操作文件系统。当用户输入"q"时,程序将保存当前文件系统状态并退出。