# 今天是六一儿童节
# 下周一就要开始课设了
# 争取在第一天给老师检查吧
# 之后一个礼拜就可以好好复习算法设计的期末考试了
# 病了两天真是难受
# 两天没有好好学习 QWQ
Part 0 前言
整个程序是在Mac os的环境下开发的,因为有一些底层系统调,Windows可能无法正常运行,不过Linux应该问题不大。
首先我是很想拿这个课设的满分的,仔细看了课设的要求,如果要拿到满分的话,需要有完整的文件系统,还要做到“命令端”和“文件系统端”的独立运行、相互通信。这个真的是比算法设计的课设工作量大多了(算法课设我就写了百来行的代码,这个估摸着得上千了)。
讲实话多进程的东西写的并不多,有写,但是也没有太多的研究这个,所以这次课设对我来说难点就在于进程间的通信了。也百度了一下Unix/Linux下的进程通信的东西,要实现的话基本就是用FIFO、消息队列来实现。
Part 1 架构
架构架构就如上图吧,FileSystem以读写的方式连接disk.dat(模拟的物理磁盘),再以IPC的方式连接各个Shell(命令端),当然这里还有很多细节就先不讨论了。
Part 2 文件系统的核心代码
这里先上一个FileSystem的实现代码(还没Debug),共413行。
FileSys.h
(6.2号更新,补了一些注释,修复了一些“肉眼可见”的Bug,因为整个文件系统还未真正运行过。)
#ifndef FileSys_h
#define FileSys_h
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define DISK_MEMO 10 * 1024 * 1024 // 磁盘总空间
#define DISK_SIZE 1024 // 每个磁盘块的大小
#define DISK_NUM DISK_MEMO / DISK_SIZE // 磁盘块的数目
#define FAT_SIZE DISK_NUM * sizeof(fatitem) // FAT表的大小
#define ROOT_DISK_NO FAT_SIZE / DISK_SIZE + 1 // 根目录起始盘块号
#define ROOT_DISK_SIZE sizeof(direct) // 目录大小
#define DIR_MAXSIZE 1024 // 路径最大长度
#define MSD 5 // 目录最大子文件数量
#define MOFN 20 // 最大打开文件
#define MAX_WRITE 1024 * 128 // 最大写入文字长度
#define FILENAME_MAXLEN 64 // 最长文件名称长度
typedef struct
{
int item; // 下一个磁盘的指针
char em_disk; // 是否空闲(‘0’为空闲,‘1’为非空闲)
} fatitem;
// FCB结构体
typedef struct
{
char name[FILENAME_MAXLEN]; // 名字
char property; // 属性(‘1’是目录,‘0’是文件)
int size; // 文件/目录 字节数
int firstdisk; // 文件/目录 起始盘块号
int next; // 子目录起始盘块号
int sign; // 根目录识别符(1为根目录标识,0为非根目录标识)
} FCB;
typedef struct
{
FCB directitem[MSD + 2];
} direct;
// 打开文件表 实例结构体
struct opentableitem
{
char name[9]; // 文件名称
int firstdisk; // 文件起始盘块号
int size; // 文件大小
};
// 打开文件 结构体
typedef struct
{
struct opentableitem openitem[MOFN];
int cur_size; // 当前打开文件的数目
} opentable;
fatitem *fat; // FAT表
direct *root; // 根目录地址
direct *cur_dir; // 当前目录
opentable u_opentable; // 文件打开表
int fd = -1; // 文件打开表的序号
char *bufferdir; // 记录当前路径的名称
char *fdisk; // 虚拟磁盘起始地址(内存中)
// FAT表数据设定函数
void fatitemset(fatitem *, int, char);
// FCB数据设定函数
void FCBset(FCB *, int, int, char *, int, char, int);
// 文件初始化,生成空的模拟磁盘物理文件(当没有disk.dat文件时执行)
void initfile();
// 模拟磁盘数据初始化
void format();
// 进入物理磁盘文件
void enter();
// 退出程序,保存物理文件
void halt();
// 新建文件
int create(char *);
// 打开文件
int open(char *);
// 关闭文件
int close(char *);
// 文件写入
int write(int, char *, int);
// 文件读取
int read(int, char *);
// 文件删除
int del(char *);
// 新建文件夹
int mkdir(char *);
// 删除文件夹
int rmdir(char *);
void initfile()
{
fdisk = (char *)malloc(DISK_MEMO * sizeof(char));
format();
}
void fatitemset(fatitem *f, int i, char e)
{
f->item = I;
f->em_disk = e;
}
void FCBset(FCB *fcb, int sign, int firstdisk, char *name, int next, char property, int size)
{
fcb->sign = sign;
fcb->firstdisk = firstdisk;
strcpy(fcb->name, name);
fcb->next = next;
fcb->property = property;
fcb->size = size;
}
void format()
{
int I;
FILE *fp;
// FAT初始化
// FAT的初始地址是disk的地址往后移 1K
fat = (fatitem *)(fdisk + DISK_SIZE);
fatitemset(&fat[0], -1, '1'); // 引导块初始化
// 1-ROOT全部置为1
for (i = 1; i < ROOT_DISK_NO - 1; I++)
fatitemset(&fat[i], i + 1, '1');
fatitemset(&fat[ROOT_DISK_NO], -1, '0');
for (i = ROOT_DISK_NO + 1; i < DISK_NUM - 1; I++)
fatitemset(&fat[i], -1, '0');
root = (direct *)(fdisk + DISK_SIZE + FAT_SIZE);
FCBset(&root->directitem[0], 1, ROOT_DISK_NO, ".", ROOT_DISK_NO, '1', ROOT_DISK_SIZE);
FCBset(&root->directitem[1], 1, ROOT_DISK_NO, "..", ROOT_DISK_NO, '1', ROOT_DISK_SIZE);
for (i = 2; i < MSD + 2; I++)
FCBset(&root->directitem[i], 0, -1, "", -1, '0', 0);
if ((fp = fopen("./disk.dat", "wb")) == NULL)
{
printf("Error: Can't open disk.");
return;
}
if ((fwrite(fdisk, DISK_MEMO, 1, fp)) != 1)
printf("Error: Disk write error!");
fclose(fp);
};
void enter()
{
FILE *fp;
int I;
fdisk = (char *)malloc(DISK_MEMO * sizeof(char));
if ((fp = fopen("./disk.dat", "rb")) == NULL)
{
printf("Error: Can't open disk.");
return;
}
if (!fread(fdisk, DISK_MEMO, 1, fp))
{
printf("Error: Can't open disk.");
exit(0);
}
fat = (fatitem *)(fdisk + DISK_SIZE);
root = (direct *)(fdisk + DISK_SIZE + FAT_SIZE);
fclose(fp);
for (i = 0; i < MOFN; I++)
{
strcpy(u_opentable.openitem[i].name, "");
u_opentable.openitem[i].firstdisk = -1;
u_opentable.openitem[i].size = 0;
}
u_opentable.cur_size = 0;
cur_dir = root;
// bufferdir = (char *)malloc(DIR_MAXSIZE * sizeof(char));
// strcpy(bufferdir, "Root:");
}
void halt()
{
FILE *fp;
int I;
if ((fp = fopen("./disk.dat", "wb")) == NULL)
{
printf("Error: Can't open disk.");
return;
}
if ((fwrite(fdisk, DISK_MEMO, 1, fp)) != 1)
printf("Error: Disk write error!");
fclose(fp);
free(fdisk);
free(bufferdir);
return;
}
int create(char *name)
{
int i, j;
if (strlen(name) > FILENAME_MAXLEN)
return -1;
for (j = 2; j < MSD + 2; j++)
if (!strcmp(cur_dir->directitem[j].name, name))
return -4;
for (i = 2; i < MSD + 2; I++)
if (cur_dir->directitem[i].firstdisk == -1)
break;
if (i > MSD + 2)
return -2;
if (u_opentable.cur_size >= MOFN)
return -3;
for (j = ROOT_DISK_NO + 1; j < DISK_NUM; j++)
if (fat[j].em_disk == '0')
break;
if (j >= DISK_NUM)
return -5;
fat[j].em_disk = '1';
FCBset(&cur_dir->directitem[i], 0, j, name, j, '0', 0);
fd = open(name);
return 0;
}
int open(char *name)
{
int i, j;
for (i = 2; i < MSD + 2; I++)
if (strcmp(cur_dir->directitem[i].name, name))
break;
if (i >= MSD + 2)
return -1;
if (cur_dir->directitem[i].property == '1')
return -4;
for (j = 0; j < MOFN; j++)
if (!strcmp(u_opentable.openitem[j].name, name))
break;
if (j < MOFN)
return -2;
if (u_opentable.cur_size >= MOFN)
return -3;
for (j = 0; j < MOFN; j++)
if (u_opentable.openitem[j].firstdisk == -1)
break;
u_opentable.openitem[j].firstdisk = cur_dir->directitem[i].firstdisk;
strcpy(u_opentable.openitem[j].name, name);
u_opentable.openitem[j].size = cur_dir->directitem[i].size;
u_opentable.cur_size++;
return j;
}
int close(char *name)
{
int I;
for (i = 0; i < MOFN; I++)
if (!strcmp(u_opentable.openitem[i].name, name))
break;
if (i >= MOFN)
return -1;
u_opentable.openitem[i].firstdisk = -1;
u_opentable.openitem[i].size = 0;
strcpy(u_opentable.openitem[i].name, "");
u_opentable.cur_size--;
return 0;
}
int write(int fd, char *buf, int len)
{
char *first;
int item, i, j, k;
int ilen1, ilen2, modlen, temp;
item = u_opentable.openitem[fd].firstdisk;
for (i = 2; i < MSD + 2; I++)
if (cur_dir->directitem[i].firstdisk == item)
break;
temp = I;
while (fat[item].item != -1)
item = fat[item].item;
// 找到文件的末地址
first = fdisk + item * DISK_SIZE + u_opentable.openitem[fd].size % DISK_SIZE;
if (DISK_SIZE - u_opentable.openitem[fd].size % DISK_SIZE > len)
{
// 最后一块磁盘剩余的大小大于要写入的大小
strcpy(first, buf);
u_opentable.openitem[fd].size += len;
cur_dir->directitem[temp].size += len;
}
else
{
for (i = 0; i < (DISK_SIZE - u_opentable.openitem[fd].size % DISK_SIZE); I++)
first[i] = buf[I];
// 剩余长度
ilen1 = len - (DISK_SIZE - u_opentable.openitem[fd].size % DISK_SIZE);
// 需要新磁盘块
ilen2 = (ilen1 - 1) / DISK_SIZE + 1;
// 最后一块磁盘的长度
modlen = ilen1 % DISK_SIZE;
for (j = 0; j < ilen2; j++)
{
for (i = ROOT_DISK_NO + 1; i < DISK_NUM; I++)
if (fat[i].em_disk == '0')
break;
if (i >= DISK_NUM)
return -1;
first = fdisk + i * DISK_SIZE;
if (j == ilen2 - 1)
for (k = 0; k < len - (DISK_SIZE - u_opentable.openitem[fd].size % DISK_SIZE - j * DISK_SIZE); k++)
first[k] = buf[k];
else
for (k = 0; k < DISK_SIZE; k++)
//这里的k是不是有问题??
first[k] = buf[k];
//这里的item是不是也有问题??
fat[item].item = I;
fat[i].em_disk = '1';
fat[i].item = -1;
}
u_opentable.openitem[fd].size += len;
cur_dir->directitem[temp].size += len;
}
return 0;
}
int read(int fd, char *buf)
{
int len = u_opentable.openitem[fd].size;
char *first;
int i, j, item;
int ilen1, modlen;
item = u_opentable.openitem[fd].firstdisk;
ilen1 = (len - 1) / DISK_SIZE + 1;
modlen = len % DISK_SIZE;
first = fdisk + item * DISK_SIZE;
for (i = 0; i < ilen1; i++)
{
if (i == ilen1 - 1)
for (j = 0; j < len - i * DISK_SIZE; j++)
buf[i * DISK_SIZE + j] = first[j];
else
{
for (j = 0; j < len - i * DISK_SIZE; j++)
buf[i * DISK_SIZE + j] = first[j];
item = fat[item].item;
first = fdisk + item * DISK_SIZE;
}
}
return 0;
}
int del(char *name)
{
int i, cur_item, item, temp;
for (i = 2; i < MSD + 2; I++)
if (!strcmp(cur_dir->directitem[i].name, name))
break;
if (i >= MSD + 2)
return -1;
cur_item = I;
if (cur_dir->directitem[cur_item].property != '0')
return -3;
for (i = 0; i < MOFN; I++)
if (!strcmp(u_opentable.openitem[i].name, name))
return -2;
item = cur_dir->directitem[cur_item].firstdisk;
while (item != -1)
{
temp = fat[item].item;
fat[item].item = -1;
fat[item].em_disk = '0';
item = temp;
}
FCBset(&cur_dir->directitem[cur_item], 0, -1, "", -1, '0', 0);
return 0;
}
int mkdir(char *name)
{
int i, j;
direct *cur_mkdir;
if (!strcmp(name, ".") || !strcmp(name, ".."))
return -4;
if (strlen(name) > FILENAME_MAX)
return -1;
for (j = 2; j < MSD + 2; j++)
if (!strcmp(cur_dir->directitem[j].name, name))
break;
if (j < MSD + 2)
return -3;
for (j = ROOT_DISK_NO + 1; j < DISK_NUM; j++)
if (fat[j].em_disk == '0')
break;
if (j > DISK_NUM)
return -5;
fat[j].em_disk = '1';
FCBset(&cur_dir->directitem[i], 0, j, name, j, '1', ROOT_DISK_SIZE);
cur_mkdir = (direct *)(fdisk + cur_dir->directitem[i].firstdisk * DISK_SIZE);
FCBset(&cur_mkdir->directitem[0], 0, cur_dir->directitem[i].firstdisk, ".", cur_mkdir->directitem[0].firstdisk, '1', ROOT_DISK_SIZE);
FCBset(&cur_mkdir->directitem[0], cur_dir->directitem[0].sign, cur_dir->directitem[0].firstdisk, "..", cur_mkdir->directitem[1].firstdisk, '1', ROOT_DISK_SIZE);
for (i = 2; i < MSD + 2; I++)
FCBset(&cur_mkdir->directitem[i], 0, -1, "", -1, '0', 0);
return 0;
}
int rmdir(char *name)
{
int i, j, item;
direct *temp_dir;
for (i = 2; i < MSD + 2; I++)
if (!strcmp(cur_dir->directitem[i].name, name))
break;
if (i >= MSD + 2)
return -1;
if (cur_dir->directitem[i].property != '1')
return -3;
temp_dir = (direct *)(fdisk + cur_dir->directitem[i].next * DISK_SIZE);
for (j = 2; j < MSD + 2; j++)
// 文件夹不为空
if (temp_dir->directitem[j].next != -1)
break;
if (j < MSD + 2)
return -2;
item = cur_dir->directitem[i].firstdisk;
fat[item].em_disk = '0';
FCBset(&cur_dir->directitem[i], 0, -1, "", -1, '0', 0);
return 0;
}
#endif
一更(6/2上午10:08)更新一下6.1号所有的工作量
先这么写着,等课设做完再把整篇文章的格式规范一下。
代码统计量截图
Part 3 “前后端”通信
截至目前已经写了1,446行代码了,但是感觉还只是完成了一个空架(真是好大的工作量啊😭)。重要的是,着1,446行代码中已经实现了用IPC协议(消息列队)实现进程通信。为什么不用命名管道(FIFO)呢,主要是FIFO在处理一对多要这种情况时,要使用这种结构:
这样子的话,假设有N个命令端的话,我们则需要建立 2+N 条FIFO,虽然说课设并不需要考虑这种极端情况,但是强迫症的我(🤦♂️)还是觉得这种做法不是很好,因为FIFO多次创建、释放容易出问题。
于是,我打算用F2S(file_system to shell)、S2F(shell to file system)来实现命令端和文件管理端的通信(理论上其实一个消息队列也可以实现)。
自定义的Message结构体
struct s2fmsg
{
long int mtype;
int serverid;
int oper;
char data[128];
};
struct f2smsg
{
long int mtype;
int oper;
int status;
char data[128];
};
命令端用S2F队列向文件系统端发送请求,文件系统用F2S向命令端发送请求结果(像极了request,原谅我的浅薄理解)。乍一看,和FIFO很像,那它是怎么实现只用两条队列呢?这里是因为消息队列比FIFO多了一个关键性的功能,就是消息队列传输的消息有一个 long int mtype
的属性,而命令端在接收文件系统端的回传信息的时候,可以根据这个long int mtype
来选择接收mtype
等于特定值的消息。基于这个,我们就可以给 f2smsg
的mtype
附加命令端的id。
(要继续写课设了,争取明天前做好)
持续更新中……
最后一次更新于6/1上午10:36
网友评论