Bootstrap

操作系统 文件与磁盘管理 C语言(模拟文件目录)

本文是在博主木子Link的代码基础上加以修改,并添加了一些文件保护功能和写入文件大小的功能,并在自己的理解上加上注释。操作系统 文件与磁盘管理 C语言(模拟文件目录)_模拟文件系统中的树型目录结构,并模拟实现常用dos命令。如:dir, md, cd, rd, cre-CSDN博客

一、设计要求

通过模拟磁盘,完成操作系统的文件管理功能,掌握包括目录结构的管理、外存空间的分配与释放以及空闲空间管理三部分。为写入模拟磁盘的数据文件建立目录,目录可以是单级文件目录、双级文件目录、树形结构目录。在目录中选择某个文件可以将其数据读入模拟内存。

1、通过初始化操作建立一个模拟磁盘,在模拟磁盘中保存目录和文件内容。创建该模拟磁盘时可创建初始的根目录内容、文件分配表。

2、文件目录项(可以采用FCB格式)应包括文件名、类型(目录 or文件)、创建日期、大小、第一个磁盘块块号。

3、目录管理需支持:

新建目录:在目录中新建空目录;

删除目录:删除空目录

为文件建立目录项:一个文件创建成功后,为该文件创建目录项,并将文件和相关信息写入目录;

删除文件:删除目录中某个文件,删除其在磁盘中的数据,并删除目录项。如果被删除文件已经读入内存应阻止删除,完成基本的文件保护。

二、设计思想

本设计采用二叉树模拟树形文件管理结构

1、文件结构如下:

所有该节点下的左子树为同一级文件夹下的目录,该节点下所有的右子树为该节点的子文件。

2、算法流程图

三、主要数据结构及其说明

1、文件结构体FCB块如下:

typedef struct FCB
{                       // 文件或目录控制块
    char name[10];      // 文件或目录名
    int size;           // 文件或目录大小,目录大小可设置为 0
    char type;          // 类型,1 为文件,2 为目录
    int first;          // 外存起始位置  -1被占 -2未被占
    char datetime[128]; // 日期时间,格式为 yyyymmdd hhmmss
    int flagg;          // 文件状态,1为打开,0为关闭
    char neirong[20];   // 文件内容
    struct FCB *next;   // 下一个兄弟节点
    struct FCB *child;  // 第一个孩子节点
    struct FCB *parent;
} F;

2、模拟内存分配情况

采用位示图模拟:随机分配1与0模拟表示内存已被占用情况:用8*8的二维数组记录,FAT块(记录文件的地址情况):用64位的一维数组记录。

例如:位示图如下

i表示行,j表示列

FAT[i * 8 + j]=-1或-2

这里的FAT块是一个一维数组,用于存储文件的位置

当位示图为0时,FAT的下标记录位示图地址,它的值表示状态。

f.size为文件大小;此时,FAT记录文件所占位示图位置。最后用一个哨兵记录该文件的起始位置并返回;

其余模拟文件管理的切换、创建、删除、查看均使用了二叉树的遍历算法。

四、核心代码

1、用FAT记录文件的位置

int findNULL(int size)
{
    int y = 0;
    int a, k = 0;
    int m[64] = {0};
    int h = 0;
    for (k = 0; k < size; k++)
    {
        a = 0;
        for (int i = 0; i < 8; i++)
        {
            for (int j = 0; j < 8; j++)
            {
                if (b[i][j] == 0)
                {
                    h = i * 8 + j; // 转化为盘块号
                    m[k] = h;
                    b[i][j] = 1;
                    FAT[h] = h;
                    a = 1;
                    break;
                }
            }
            if (a == 1)
            {
                break;
            }
        }
    }
    return m[0];//返回该文件的起始位置
}

2、输出文件的地址

void fatShow() // 展示文件分配表
{
    int j = 0;
    for (int i = 0; i < 8; i++)
    {
        for (int j = 0; j < 8; j++)
        {
            printf("%d ", FAT[i * 8 + j]);
        }
        printf("\n");
    }
    printf("***\n");
    F *np = p; // 指向当前节点
    while (np->parent != NULL)
    {
        np = np->parent;
    }
    inOrder(np);//将当前节点传给inOrder()函数,用于遍历二叉树下所有文件
}
// 遍历
void inOrder(F *root)
{
    if (root != NULL)
    {
        inOrder(root->child);
        if (root->type == 1)//如果节点类型为文件类型,就输出该节点
        {
            printf("%s ", root->name);
            printf("文件的地址 ");
            int k = 0;
            int i = root->first;
            for (i; i < 64; i++)
            {

                if (FAT[i] != -1 && FAT[i] != -2 && k < root->size)
                {
                    printf("%d ", FAT[i]);
                    k++;//用于记录文件长度
                }
                if (k == root->size)//当输出位置达到文件长度跳出该循环即可输出下一个文件地址
                {
                    printf("\n");
                    break;
                }
            }
        }
        inOrder(root->next);
    }
}

3、文件保护 

root->flagg用于记录文件状态。这里同样用到的是二叉树的递归遍历

void odr(F *root, char *name)
{
    if (root != NULL)
    {
        odr(root->child, name);//左
        {
            if (strcmp(root->name, name) == 0)//中
            {
                printf("close为关闭文件,r为读文件\n");
                char w[8];
                scanf("%s", w);
                if (strcmp(w, "r") == 0)
                {
                    root->flagg = 1;
                    printf("%s\n", root->neirong);
                }
                if (strcmp(w, "close") == 0)
                {
                    root->flagg = 0;
                    printf("关闭成功\n");
                }
            }
        }
        odr(root->next, name);//右
    }
}
void ZT(char *name)
{

    F *np = p;
    odr(np, name);
}

其它文件切换,删除目录文件都用上了二叉树的遍历,这里就不赘述了。

4、所有代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
typedef struct FCB
{                       // 文件或目录控制块
    char name[10];      // 文件或目录名
    int size;           // 文件或目录大小,目录大小可设置为 0
    char type;          // 类型,1 为文件,2 为目录
    int first;          // 外存起始位置  -1被占 -2未被占
    char datetime[128]; // 日期时间,格式为 yyyymmdd hhmmss
    int flagg;          // 文件状态,1为打开,0为关闭
    char neirong[20];   // 文件内容
    struct FCB *next;   // 下一个兄弟节点
    struct FCB *child;  // 第一个孩子节点
    struct FCB *parent;
} F;

F *p = NULL;                           // 指向当前目录
int b[100][100];                       // 位视图
int FAT[100];                          // 文件分配表
void updateTable(int first, int size); // 更新fat表
int findNULL(int size);
void getTime(F *f) // 获取当前时间并保存
{
    time_t t;
    char buf[128];
    memset(buf, 0, sizeof(buf));
    struct tm *tmp;
    t = time(NULL);
    tmp = localtime(&t);
    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tmp);
    for (int i = 0; i < 128; i++)
    {
        f->datetime[i] = buf[i];
    }
}
void byteCreat() // 随机分配空间
{
    srand((unsigned)time(NULL));
    for (int i = 0; i < 8; i++)
    {
        for (int j = 0; j < 8; j++)
        {
            b[i][j] = rand() % 2;
            printf("%d ", b[i][j]);
            if (b[i][j] == 0)
            {
                FAT[i * 8 + j] = -2; // 未被占;
            }
            else if (b[i][j] == 1)
            {
                FAT[i * 8 + j] = -1; //-1:被占
            }
        }
        printf("\n");
    }
}
void byteShow() // 展示位示图
{
    for (int i = 0; i < 8; i++)
    {
        for (int j = 0; j < 8; j++)
        {
            printf("%d ", b[i][j]);
        }
        printf("\n");
    }
}
// 遍历
void inOrder(F *root)
{
    if (root != NULL)
    {
        inOrder(root->child);
        if (root->type == 1)
        {
            printf("%s ", root->name);
            printf("文件的地址 ");
            int k = 0;
            int i = root->first;
            for (i; i < 64; i++)
            {

                if (FAT[i] != -1 && FAT[i] != -2 && k < root->size)
                {
                    printf("%d ", FAT[i]);
                    k++;
                }
                if (k == root->size)
                {
                    printf("\n");
                    break;
                }
            }
        }
        inOrder(root->next);
    }
}
void fatShow() // 展示文件分配表
{
    int j = 0;
    for (int i = 0; i < 8; i++)
    {
        for (int j = 0; j < 8; j++)
        {
            printf("%d ", FAT[i * 8 + j]);
        }
        printf("\n");
    }
    printf("***\n");
    F *np = p; // 指向当前节点
    while (np->parent != NULL)
    {
        np = np->parent;
    }
    inOrder(np);
}

void makedir(int a) // 创建文件(夹)
{

    char name[10];
    F *np = p; // 指向当前目录
    scanf(" %s", name);
    F *f = (F *)malloc(sizeof(F)); // 新建空白目录
    strcpy(f->name, name);         // 保存名字
    if (a == 1)                    // 建立文件,1为文件 2 为目录
    {
        f->type = 1;
        printf("请输入文件大小");
        scanf("%d", &f->size);
        printf("输入文件内容:");
        char neirong[50];
        scanf(" %s", neirong);
        strcpy(f->neirong, neirong);
        f->first = findNULL(f->size); // 文件的起始地址
        f->flagg = 0;
    }
    if (a == 2) // 目录
    {
        f->type = 2;
        f->first = -2;
        f->size = 0;
        f->flagg = 0;
    }
    getTime(f);
    f->child = NULL;
    f->next = NULL;
    f->parent = np; // 父节点
    if (np->child == NULL)
    {
        np->child = f;
    }
    else // 进入当前子目录,左孩子为同一级文件或目录,它的其它兄弟节点为它的子目录或文件
    {
        np = np->child; // 指向第一个目录或文件
        if (a == 2)
        {
            if (np->type == 2)
            {
                while (np->next != NULL)
                {
                    if (np->type == 2 && np->next->type == 1) // 目录新建在目录和文件中间
                    {
                        break;
                    }
                    np = np->next;
                }
                f->next = np->next;
                np->next = f;
            }
        }
        else if (a == 1)
        {
            while (np->next != NULL) // 为空跳出循环
            {
                np = np->next;
            }
            f->next = np->next;
            np->next = f;
        }
    }
}
int findNULL(int size)
{
    int y = 0;
    int a, k = 0;
    int m[64] = {0};
    int h = 0;
    for (k = 0; k < size; k++)
    {
        a = 0;
        for (int i = 0; i < 8; i++)
        {
            for (int j = 0; j < 8; j++)
            {
                if (b[i][j] == 0)
                {
                    h = i * 8 + j; // 转化为盘块号
                    m[k] = h;
                    b[i][j] = 1;
                    FAT[h] = h;
                    a = 1;
                    break;
                }
            }
            if (a == 1)
            {
                break;
            }
        }
    }
    return m[0];
}
void cdir(char *name) // 切换目录
{
    F *np = p;
    np = np->child;
    if (strcmp(np->name, name) == 0) // 名字相同
    {
        p = np; // 找到并进入
    }
    else
    {
        while (strcmp(np->name, name) != 0)
        {
            np = np->next;
            if (strcmp(np->name, name) == 0)
            {
                p = np; // 找到并进入
                break;
            }
            if (np == NULL)
            {
                printf("未找到此目录");
                break;
            }
        }
    }
}
void printNow() // 展示当前目录下的文件及文件夹
{
    F *np = p;
    if (p->child == NULL)
    {
        printf("当前文件夹为空\n");
    }
    else
    {
        np = np->child;
        printf("%s    <DIR>      .\n", np->parent->datetime); // 当前目录建立的时间,或者进入终端时间
        // printf("%s    <DIR>      ..\n", np->parent->datetime);
        while (np != NULL)
        {
            printf("%s 类型:%d 名字:%s 大小:%d\n", np->datetime, np->type, np->name, np->size);
            np = np->next;
        }
    }
}
void delDir(char *name) // 删除目录
{
    int flag = 0;
    F *np = p;
    if (np->child == NULL)
        printf("无此目录\n");
    else
    {
        np = np->child;
        if (strcmp(np->name, name) == 0 && np->child == NULL) // 为第一个
        {
            p->child = np->next;
            free(np);
            printf("成功删除此目录\n");
            flag = 1;
        }
        else
        {
            while (np->next != NULL) // 文件夹下还有目录
            {
                if (strcmp(np->next->name, name) == 0 && np->next->child == NULL)
                {
                    np->next = np->next->next;
                    flag = 1;
                    printf("成功删除此目录\n");
                    break;
                }
                np = np->next;
            }
        }
        if (flag == 0)
        {
            printf("删除失败!无当前目录或当前目录不为空\n");
        }
    }
}
void delFile(char *name) // 删除文件
{
    int flag = 0;
    F *np = p;
    if (np->child == NULL)
        printf("无此文件\n");
    else
    {
        np = np->child;
        if (strcmp(np->name, name) == 0 && np->flagg == 0) // 为第一个
        {
            p->child = np->next;

            printf("成功删除此文件\n");
            updateTable(np->first, np->size); // 输入文件起始位置,更新fat表
            free(np);
            flag = 1;
        }
        else
        {
            while (np->next != NULL)
            {
                if (strcmp(np->next->name, name) == 0 && np->flagg == 0)
                {
                    updateTable(np->next->first, np->next->size); // 更新fat表
                    np->next = np->next->next;
                    flag = 1;
                    printf("成功删除此文件\n");
                    break;
                }
                np = np->next;
            }
        }
        if (flag == 0 || np->flagg == 1)
        {
            printf("删除失败!无当前目录或当前目录不为空或该文件正打开\n");
        }
    }
}
void updateTable(int first, int size) // 更新fat表
{
    int k = 0;
    for (int i = 0; i < 64; i++)
    {
        if (FAT[i] != -1 && FAT[i] != -2)
        {
            FAT[i] = -2;
            k++;
            b[i / 8][i % 8] = 0;
        }
        if (k == size)
            break;
    }
}
void updatelujing() // 更新当前所在位置
{
    F *np = p;
    F a[100];
    int count = 0;
    while (np->parent != NULL)
    {
        a[count] = *np;
        np = np->parent;
        count++;
    }
    for (int i = count - 1; i > 0; i--)
    {
        printf("%s\\", a[i].name);
    }
    printf("%s\\", p->name);
    printf(">");
}
void meum()
{
    printf("  ******************************************************\n\n");
    printf("  *                欢迎使用文件模拟系统                 *\n \n");
    printf("  ******************************************************\n\n");
    printf("  该系统目前支持如下功能:\n");
    printf("  1.切换到当前路径下的某文件夹: cd + 文件夹\n");
    printf("  2.切换到当前路径的上级文件夹: cd ..\n");
    printf("  3.在任意路径下切换回根路径ROOT: cd \\ \n");
    printf("  4.列出当前路径下的全部文件夹和文件: dir\n");
    printf("  5.在当前路径下新建文件夹: md + 文件夹名\n");
    printf("  6.在当前路径下新建文件: mk + 文件名\n");
    printf("  7.位示图: show\n");
    printf("  8.删除当前路径下的某文件名: del+文件名\n");
    printf("  9.文件分配表:fat\n");
    printf("  10.删除文件夹:rd+文件夹名\n");
    printf("  11.操作文件oper\n");
    printf("  ******************************************************\n");
}
void odr(F *root, char *name)
{
    if (root != NULL)
    {
        odr(root->child, name);
        {
            if (strcmp(root->name, name) == 0)
            {
                printf("close为关闭文件,r为读文件\n");
                char w[8];
                scanf("%s", w);
                if (strcmp(w, "r") == 0)
                {
                    root->flagg = 1;
                    printf("%s\n", root->neirong);
                }
                if (strcmp(w, "close") == 0)
                {
                    root->flagg = 0;
                    printf("关闭成功\n");
                }
            }
        }
        odr(root->next, name);
    }
}
void ZT(char *name)
{

    F *np = p;
    odr(np, name);
}
int main()
{
    meum();
    char a[6];
    char f_name[10];
    int flag = 1;
    F *f = (F *)malloc(sizeof(F));
    strcpy(f->name, "root");
    f->type = 2;
    f->size = 0;
    getTime(f);
    f->child = NULL;
    f->next = NULL;
    f->parent = NULL;
    f->first = -2;
    p = f; // p指向当前目录
    byteCreat();
    // head = (node *)malloc(sizeof(node));
    // head->next = NULL;
    while (flag)
    {
        do
        {
            updatelujing();
            scanf(" %s", a);
            if (strcmp(a, "md") == 0) // 创建空目录
            {
                makedir(2);
            }
            else if (strcmp(a, "cd") == 0)
            {
                // printf("请输入目录名\n");
                scanf(" %s", f_name);
                cdir(f_name);
            }
            else if (strcmp(a, "rd") == 0) // 删除空目录
            {
                // printf("请输入需要删除的目录名\n");
                scanf(" %s", f_name);
                delDir(f_name);
            }
            else if (strcmp(a, "mk") == 0) // 创建文件
            {
                makedir(1);
            }
            else if (strcmp(a, "del") == 0) // 删除文件
            {
                // printf("请输入需要删除的文件名\n");
                scanf(" %s", f_name);
                delFile(f_name);
            }
            else if (strcmp(a, "dir") == 0) // 列出目录信息
            {
                printNow();
            }
            else if (strcmp(a, "q") == 0)
            {
                flag = 0;
            }
            else if (strcmp(a, "show") == 0) // 展示位示图
            {
                byteShow();
            }
            else if (strcmp(a, "fat") == 0) // 展示fat表
            {
                fatShow();
            }
            else if (strcmp(a, "cd..") == 0) // 返回上一级目录
            {
                p = p->parent;
            }
            else if (strcmp(a, "oper") == 0)
            {
                printf("请输入文件名\n");
                scanf("%s", f_name);
                ZT(f_name);
            }
        } while (flag == 1);
    }
    return 0;
}

五、运行结果

1、建立文件夹如下图所示

菜单如下图:

0/1矩阵为位示图,-1/-2为FAT块,记录了文件在位示图的位置,这里为了方便观察,一维数组FAT将以矩阵的形式输出:

2、文件保护

进行读的文件操作,此时文件打开,无法删除文件。需要关闭文件才可以进行删除操作

输入关闭文件的指令,即可删除文件如下图所示

3、建立目录(类型2为目录)

4、建立多个文件及目录: 

5、显示该目录下的文件及文件夹:

;