计算机体系结构与操作系统课设——bochs实现文件系统

🧡🧡准备工作🧡🧡

此次课设是基于之前学习的bochs的,这里需要配置好bochs环境和相关命令操作。
参考我之前写的博客

  • bochs安装+安装hd60M盘:https://blog.csdn.net/luohaojia123/article/details/130854714
  • 降低版本gcc为4.4:https://blog.csdn.net/luohaojia123/article/details/131700018
  • 安装hd80M盘: 参考一位博主:https://blog.csdn.net/kanshanxd/article/details/132026480?spm=1001.2014.3001.5502

🧡🧡相关知识🧡🧡

先明确几个概念

  • inode:记录文件与磁盘位置的映射。磁盘分区内会有一个inode数组,根据文件记录的inode数组下标,即可查找对应inode信息。
  • 目录:特殊的文件,由目录项构成,目录项记录了文件名与inode的映射。
  • 超级块:在磁盘分区的第二个位置,包含了关于inode、目录、位图等元数据信息。在这里插入图片描述
  • 文件结构:构成全局文件结构数组——文件表,描述文件与进程的操作状态。
  • 文件描述符:在进程的PCB中,指向文件表中的某个特定文件。
    在这里插入图片描述

主要思路:(例如访问/home/test.c)
(超级块------>根目录------->具体文件)

  1. 由于超级块位置固定,我们可以去磁盘中直接访问。
  2. 从超级块中,我们能知道根目录的inode标号和inode数组的位置。
  3. 使用根目录的inode标号与inode数组位置,我们可以找到根目录文件对应的inode,然后在磁盘中找到根目录文件。
  4. 在根目录文件中,我们查找一个名叫home的目录项,从中取出home的inode数组标号。
  5. 使用home的inode标号,我们再次访问inode数组,找到home目录文件在磁盘上的实际位置。
  6. 最后,在home目录文件中,我们查找一个名叫test.c的目录项,从中取出test.c的inode数组标号,进而找到test.c在磁盘上的位置。

🧡🧡基本实现🧡🧡

书上内容实现功能是逐步完善的,也即n文件夹是最终实现的文件系统,具体每一步是如何实现的这里直接参考(很详细):https://blog.csdn.net/kanshanxd/article/details/132521696
在这里插入图片描述

关于makefile文件,这里在博主基础上:

  • 首先记得把hd60M盘位置改为自己的路径:在这里插入图片描述
  • 将bochs的启动命令也封装在mak all命令中,记得依据自己盘的路径进行配置,提高便利性(命令行中每次输入make all命令,即可将文件系统功能烧录到hd60M盘,同时直接启动bochs)
    在这里插入图片描述
    在这里插入图片描述

完整makefile文件配置

#定义一大堆变量,实质就是将需要多次重复用到的语句定义一个变量方便使用与替换
BUILD_DIR=./build
ENTRY_POINT=0xc0001500
HD60M_PATH=/home/lhj/Public/bochs/hd60M.img
#只需要把hd60m.img路径改成自己环境的路径,整个代码直接make all就完全写入了,能够运行成功
AS=nasm
CC=gcc-4.4
LD=ld
LIB= -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ -I thread/ -I userprog/	-I fs/
ASFLAGS= -f elf -g
CFLAGS= -Wall $(LIB) -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes -m32 -fno-stack-protector -g
#-Wall warning all的意思,产生尽可能多警告信息,-fno-builtin不要采用内部函数,
#-W 会显示警告,但是只显示编译器认为会出现错误的警告
#-Wstrict-prototypes 要求函数声明必须有参数类型,否则发出警告。-Wmissing-prototypes 必须要有函数声明,否则发出警告

LDFLAGS= -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map -m elf_i386
#-Map,生成map文件,就是通过编译器编译之后,生成的程序、数据及IO空间信息的一种映射文件
#里面包含函数大小,入口地址等一些重要信息

OBJS=$(BUILD_DIR)/main.o $(BUILD_DIR)/init.o \
	$(BUILD_DIR)/interrupt.o $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o \
	$(BUILD_DIR)/print.o $(BUILD_DIR)/debug.o $(BUILD_DIR)/string.o $(BUILD_DIR)/bitmap.o \
	$(BUILD_DIR)/memory.o $(BUILD_DIR)/thread.o	$(BUILD_DIR)/list.o	$(BUILD_DIR)/switch.o \
	$(BUILD_DIR)/sync.o	$(BUILD_DIR)/console.o $(BUILD_DIR)/keyboard.o $(BUILD_DIR)/ioqueue.o \
	$(BUILD_DIR)/tss.o	$(BUILD_DIR)/process.o	$(BUILD_DIR)/syscall.o $(BUILD_DIR)/syscall-init.o \
	$(BUILD_DIR)/stdio.o $(BUILD_DIR)/stdio-kernel.o $(BUILD_DIR)/ide.o $(BUILD_DIR)/fs.o $(BUILD_DIR)/inode.o \
	$(BUILD_DIR)/file.o $(BUILD_DIR)/dir.o
#顺序最好是调用在前,实现在后

######################编译两个启动文件的代码#####################################
boot:$(BUILD_DIR)/mbr.o $(BUILD_DIR)/loader.o
$(BUILD_DIR)/mbr.o:boot/mbr.S
	$(AS) -I boot/include/ -o build/mbr.o boot/mbr.S
	
$(BUILD_DIR)/loader.o:boot/loader.S
	$(AS) -I boot/include/ -o build/loader.o boot/loader.S
	
######################编译C内核代码###################################################
$(BUILD_DIR)/main.o:kernel/main.c
	$(CC) $(CFLAGS) -o $@ $<	
# $@表示规则中目标文件名的集合这里就是$(BUILD_DIR)/main.o  $<表示规则中依赖文件的第一个,这里就是kernle/main.c 

$(BUILD_DIR)/init.o:kernel/init.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/interrupt.o:kernel/interrupt.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/timer.o:device/timer.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/debug.o:kernel/debug.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/string.o:lib/string.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/bitmap.o:lib/kernel/bitmap.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/memory.o:kernel/memory.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/thread.o:thread/thread.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/list.o:lib/kernel/list.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/sync.o:thread/sync.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/console.o:device/console.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/keyboard.o:device/keyboard.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/ioqueue.o:device/ioqueue.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/tss.o:userprog/tss.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/process.o:userprog/process.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/syscall.o:lib/user/syscall.c
	$(CC) $(CFLAGS) -o $@ $<
$(BUILD_DIR)/syscall-init.o:userprog/syscall-init.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/stdio.o:lib/stdio.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/stdio-kernel.o:lib/kernel/stdio-kernel.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/ide.o:device/ide.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/fs.o:fs/fs.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/inode.o:fs/inode.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/file.o:fs/file.c
	$(CC) $(CFLAGS) -o $@ $<

$(BUILD_DIR)/dir.o:fs/dir.c
	$(CC) $(CFLAGS) -o $@ $<
###################编译汇编内核代码#####################################################
$(BUILD_DIR)/kernel.o:kernel/kernel.S 
	$(AS) $(ASFLAGS) -o $@ $<

$(BUILD_DIR)/print.o:lib/kernel/print.S
	$(AS) $(ASFLAGS) -o $@ $<

$(BUILD_DIR)/switch.o:thread/switch.S
	$(AS) $(ASFLAGS) -o $@ $<
##################链接所有内核目标文件##################################################
$(BUILD_DIR)/kernel.bin:$(OBJS)
	$(LD) $(LDFLAGS) -o $@ $^
# $^表示规则中所有依赖文件的集合,如果有重复,会自动去重

.PHONY:mk_dir hd clean build all boot gdb_symbol	#定义了7个伪目标
mk_dir:
	if [ ! -d $(BUILD_DIR) ];then mkdir $(BUILD_DIR);fi 
#判断build文件夹是否存在,如果不存在,则创建

hd:
	dd if=build/mbr.o of=$(HD60M_PATH) count=1 bs=512 conv=notrunc && \
	dd if=build/loader.o of=$(HD60M_PATH) count=4 bs=512 seek=2 conv=notrunc && \
	dd if=$(BUILD_DIR)/kernel.bin of=$(HD60M_PATH) bs=512 count=200 seek=9 conv=notrunc
	
clean:
	@cd $(BUILD_DIR) && rm -f ./* && echo "remove ./build all done"
#-f, --force忽略不存在的文件,从不给出提示,执行make clean就会删除build下所有文件

build:$(BUILD_DIR)/kernel.bin
#执行build需要依赖kernel.bin,但是一开始没有,就会递归执行之前写好的语句编译kernel.bin

run_bochs:
	cd /home/lhj/Public/bochs/bin && ./bochs -f lhjbochsrc.disk

#生成可以被GDB理解的符号表,用于GDB调试
gdb_symbol:
	objcopy --only-keep-debug $(BUILD_DIR)/kernel.bin $(BUILD_DIR)/kernel.sym

all:mk_dir boot build hd run_bochs
#make all 就是依次执行mk_dir build hd run_bochs

🧡🧡综合实现🧡🧡

在完成书本上基本实现后,为了更好地演示文件系统所有功能,我重新编写/kernel/main函数,以在bochs窗口中能清晰看到文件系统的各个功能实现。

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"
#include "string.h"
#include "dir.h"

void director_operator(const char *op, const char *pathname);
void file_operator(const char *op, const char *pathname, const char *w_con);
int main(void)
{
       put_str("I am kernel\n");
       init_all();

       // show root director
       director_operator("show","/");

       // create /dir1
       director_operator("create","/dir1");
       
       // create /dir1/file2 
       char file2[]="/dir1/file2";
       file_operator("create",file2,"");

       // show  /dir1 content
       director_operator("show","/dir1");

       // write file2
       file_operator("write",file2,"123");

       // read file
       file_operator("read",file2,"");

       
       // delete file
       file_operator("delete",file2,"");

       // get file attr
       director_operator("get_attr","/");

       // show  /dir1 content
       director_operator("show","/dir1");

       // delete director
       // director_operator("delete","/dir1");

       // show root content
       sys_rewinddir(sys_opendir("/"));
       director_operator("show","/");

       while (1);
       return 0;
}
void director_operator(const char *op, const char *pathname){
       if( strcmp(op,"create")==0 ){
              printf("==========create dir: %s==========\n",pathname);
              printf("      %s create %s!\n", pathname, sys_mkdir(pathname) == 0 ? "done" : "fail");

       }else if( strcmp(op,"show")==0 ){
              printf("==========show director: %s ===========\n", pathname);
              struct dir *dir = sys_opendir(pathname);
              char *type = NULL;
              struct dir_entry *dir_e = NULL;
              while ((dir_e = sys_readdir(dir)))
              {
                     if (dir_e->f_type == FT_REGULAR){
                            type = "regular";
                     }
                     else{
                            type = "directory";
                     }
                     printf("      %s: %s", type, dir_e->filename);
              }
              printf("\n");
              sys_close(dir);
       }else if( strcmp(op,"delete")==0 ){
              printf("==========delete director: %s===========\n",pathname);
              if (sys_rmdir(pathname) == 0)
              {
                     printf("%s delete done!\n",pathname);
              }
       }else if( strcmp(op,"get_attr")==0 ){
              printf("==========get %s attr===========\n",pathname);
              struct stat obj_stat;
              sys_stat(pathname, &obj_stat);
              printf("      inode: %d\n      size: %d\n        filetype: %s\n",
                     obj_stat.st_ino, 
                     obj_stat.st_size,
                     obj_stat.st_filetype == 2 ? "directory" : "regular");
       }
              
}
void file_operator(const char *op, const char *pathname, const char *w_con){
       if( strcmp(op,"create")==0 ){
              printf("==========create file: %s===========\n", pathname);
              uint32_t fd = sys_open(pathname, O_CREAT);
              if(fd!=-1){
                     printf("      %s create done!\n",pathname);
              }
              sys_close(fd);

       }else if( strcmp(op,"write")==0 ){
              printf("==========write %s===========\n", pathname);
              int con_len = strlen(w_con);
              uint32_t fd = sys_open(pathname, O_RDWR);
              sys_write(fd, w_con, con_len);
              sys_close(fd);

       }else if( strcmp(op,"read")==0 ){
              printf("==========read %s===========\n",pathname);
              uint32_t fd = sys_open(pathname, O_RDWR);
              char buf[64]={0};
              int read_bytes = sys_read(fd, buf, 20);
              printf("      file content: %s\n", buf);
              sys_close(fd);

       }else if( strcmp(op,"delete")==0 ){
              printf("==========delete %s===========\n",pathname);
              printf("      %s delete %s!\n", pathname, sys_unlink(pathname) == 0 ? "done" : "fail");
       }  
}

解释这两个函数:

  • director_operator 函数
    此函数用于处理目录(文件夹)相关的操作。它根据提供的操作类型 (op) 和路径名 (pathname) 执行相应的动作。
    - 输入参数:
    - op: 字符串,表示要执行的操作类型,比如 “create”, “show”, “delete”, “get_attr”。
    - pathname: 字符串,表示目录的路径。
    - 操作类型:
    - “create”: 创建一个新的目录。使用 sys_mkdir 系统调用来创建目录,并打印操作结果。
    - “show”: 显示目录内容。使用 sys_opendir 和 sys_readdir 系统调用来遍历目录中的文件和子目录,并打印它们的类型(普通文件或目录)和名称。
    - “delete”: 删除目录。使用 sys_rmdir 系统调用来删除目录,并打印操作结果。
    - “get_attr”: 获取目录的属性。使用 sys_stat 系统调用来获取目录的属性,如 inode 编号、大小和文件类型,并打印这些信息。
  • file_operator 函数
    此函数用于处理文件相关的操作。根据提供的操作类型 (op)、文件路径 (pathname) 和在写操作时需要的内容 (w_con) 执行相应的动作。
    - 输入参数:
    - op: 字符串,表示要执行的操作类型,比如 “create”, “write”, “read”, “delete”。
    - pathname: 字符串,表示文件的路径。
    - w_con: 字符串,表示写入文件的内容(只在 op 为 “write” 时使用)。
    - 操作类型:
    - “create”: 创建一个新文件。使用 sys_open 系统调用以创建模式打开文件,并打印操作结果。然后关闭文件描述符
    - “write”: 向文件写入内容。使用 sys_open 系统调用以读写模式打开文件,然后使用 sys_write 写入内容,并关闭文件描述符
    - “read”: 读取文件内容。使用 sys_open 系统调用以读写模式打开文件,然后使用 sys_read 读取内容,并关闭文件描述符
    - “delete”: 删除文件。使用 sys_unlink 系统调用来删除文件,并打印操作结果

实现效果:

在这里插入图片描述
根据图中运行结果的数字:
1.首先展示根目录/:
结果显示:在这里插入图片描述

分析:原先我已经在磁盘中根目录建立了一个目录dir1和一个文件file1,所以这里会有初始文件和目录。
对应代码:上述main代码中的在这里插入图片描述
,调用自己额外封装的目录操作director_operator函数,参数”show”表示展示目录内容,”/”表示要展示的目录路径名称。在其内部,它使用 sys_opendir 打开指定的目录,通过 sys_readdir 循环读取目录中的每个条目,并打印出每个文件或子目录的类型和名称。 这两个函数在路径为:综合所有功能/fs/fs.c里实现,上述已经阐述了它的实现流程,这里不再赘述,下同。

2.尝试在根目录下创建一个新目录dir1:
结果显示:在这里插入图片描述

分析: 因为根目录下已经存在目录dir1了,所以提示创建失败。
对应代码:对应上述main代码中的在这里插入图片描述
,参数”create”表示创建目录内容,”/dir1”表示要创建的目录路径名称。在其内部,它使用 sys_mkdir 函数来创建指定路径的目录,并打印操作的结果。

3.尝试在/dir1目录下新建文件file2:
结果显示:在这里插入图片描述

分析:先扫描了/dir1下目录是否存在file2文件,然后再进行创建。
对应代码:对应上述main代码中的在这里插入图片描述
,先创建一个file2变量,方便后续使用,然后调用文件操作函数file_operator,”create”表示创建文件,”/dir1/file2”表示要创建的文件路径。在其内部,它使用 sys_open 函数以创建模式打开文件,然后关闭文件描述符。

4.展示/目录dir1内容:
结果显示:在这里插入图片描述

分析: file2是上述步骤创建的, . 和 … 表示当面目录项和根目录项。
对应代码:对应上述main代码中的在这里插入图片描述
,参数 “show” 表示显示目录内容的操作,“/dir1” 表示要显示内容的目录路径。在其内部,它使用 sys_opendir 和 sys_readdir 来遍历目录并打印出目录中的文件和子目录。

5.对/dir1/file2写入内容:
结果显示:在这里插入图片描述

分析:表示写入的块地址,方便我们可以使用xxd命令去验证它是否真的有写入。
对应代码:对应上述main代码中的在这里插入图片描述
,参数 “write” 表示写入文件内容的操作,file2(路径 “/dir1/file2”)表示目标文件的路径,而 “123” 是要写入的内容。在其内部,它使用 sys_open 打开文件,然后使用 sys_write 将内容写入文件。

6.读取/dir1/file2的内容:
结果显示:在这里插入图片描述

分析:这是上一步骤写入的内容,因此表明写入和读取成功。
对应代码:在这里插入图片描述
,参数 “read” 表示读取文件内容的操作,file2(路径 “/dir1/file2”)表示要读取内容的文件路径。在其内部,它使用 sys_open 打开文件,然后使用 sys_read 读取内容,并打印出来。

7.删除文件/dir1/file2:
结果显示:在这里插入图片描述

分析:表明文件/dir1/file2删除成功,到目前,file2分析:文件在一次程序中,经历了创建、写入、读出、删除,因此下次再次运行该main程序时,依然会重新创建file2。
对应代码:在这里插入图片描述
,参数 “delete” 表示删除文件的操作,file2(路径 “/dir1/file2”)表示要删除的文件路径。在其内部,它使用 sys_unlink 函数来删除指定的文件,并打印操作的结果。

8.获取根目录/的属性:
结果显示:在这里插入图片描述

分析:作为根目录。其inode为执行inode数组的第一个,因此其inode=0;而size=96,是因为根目录下有四个目录项:. 、 … 、 dir1 、 file1 ,而每个目录项占24个字节,因此总共大小为96字节。
对应代码:在这里插入图片描述
,参数 “get_attr” 表示获取目录属性的操作,“/” 表示要获取属性的目录路径。在其内部,它使用 sys_stat 函数来获取目录的属性,如 inode 编号、大小和文件类型,并打印这些信息。

9.再次展示目录/dir1的所有目录项:
结果显示:在这里插入图片描述

分析:我们在第3步和第7步分别对/dir1/file2进行了创建和删除,因此最终/dir1目录下只有本目录项和根目录项。
对应代码:在这里插入图片描述
,这是再次显示 “/dir1” 目录内容的操作。它使用 sys_opendir 和 sys_readdir 来遍历目录并打印出目录中的文件和子目录。

10.再次展示根目录/的所有目录项
结果显示:在这里插入图片描述

分析:在此测试程序中,没有对file1进行有关操作,主要是对/dir1里的file2进行操作,因此根目录内容同1依旧不变。
对应代码:在这里插入图片描述
,首先使用 sys_rewinddir 和 sys_opendir 重置并打开根目录的读取指针,然后使用 director_operator 显示根目录的内容,参数 “show” 和路径 “/” 作用如之前所述。

🧡🧡总结🧡🧡

通过这次课程设计,从理论到实践的转变,使我得以将书本上抽象的概念具体实现。
从知识理解方面:对文件系统中相关定义的有了更加深刻的理解,如inode、超级块、文件结构、文件描述符、目录项等,另外,不仅要理解它们的定义,我觉得还很关键的是要熟知它们的所在位置(内存、磁盘、进程等等),实现文件系统本质就是在内存和磁盘上对这些概念进行来回交互。
从代码实践的角度:跟着原书参考和博客讲解,每个小节的内容是逐步完善的,稍微比较麻烦的便是c小节创建文件,因为要声明许多相关的辅助操作函数,而其他小节模块的实现本身其实并不难。源码虽多,其实每个模块可以主要分为:底层逻辑实现+sys_封装实现,从而在main.c中直接调用sys_系列的操作函数,这样提高了测试代码的可读性和编写性,从而实现各种文件操作。最后,在完成给的的实验任务上,我为了更加充分地理解各个模块的实现功能,自行将a-n小节的模块功能进行结合。在结合过程中,也遇到不少麻烦,最主要的bug是sys_open打开文件后,没有及时关闭文件就进行了下一步操作。同时,在调试过程中,鉴于bochs的特性,基于目前已学的知识,并不能实现像平常C++语言出现的黑框进行交互从而方便测试,只能一次一次地make all编译,因此增加了调试的难度。另外,由于某些模块的功能代码比较繁杂,如遍历目录的功能,它涉及sys_opendir、sys_readdir、sys_closedir,而且一次程序中肯定要前后展示两三次目录,这样下来代码的可读性比较差,且有冗余重复的代码段,因此为了再增强代码的可读性,我又将sys_系列的相关操作函数进行了一层封装,大大方便了代码调试和测试功能。

  • 21
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值