当前位置: 首页 > news >正文

【Create my OS】8 文件系统

Github代码仓库链接

本章将实现一个很基础的文件系统,并实现一些简单的内核文件操作,最终,我们将用从文件系统读入 ELF 文件的方式来创建进程。

8.1 SimpleFS

1、本节将对 Unix File System 的一个简单的实现——SimpleFileSystem 进行魔改,但是依旧沿用这个名字,因为这个文件系统足够 simple

在这里插入图片描述

2、超级块

typedef struct {uint32 magic;               // 魔数uint32 blocks;              // 总磁盘块数uint32 unusedBlocks;        // 未使用的磁盘块数uint32 freemapBlocks;       // freemap 块数uint8 info[32];             // 其他信息
} SuperBlock;
  • 第一个字段就是 magic number。这个字段恒为 0x4D534653,即 ASCII 码 “M”、“S”、“F”、“S”。在操作系统初始化文件系统时,会首先检查魔数以确定文件系统类型
  • blocks 字段表示这个文件系统一共占用多少个磁盘块
  • unusedBlocks 字段表示这个文件系统目前剩余的空闲磁盘块个数
  • freemapBlocks 表示 Freemap 块的总个数,这个字段的更重要用处在于推断出 Root Inode 所在的磁盘块号。
  • 最后就是 info 字段,这是一个字符串,记录了一些其他信息。
  • 超级块的实际内容很小,只有 48 字节,超级块的剩余空间由 0 填充。
    3、Inode ,一个 Inode 块代表了一个文件或文件夹,结构如下:
typedef struct
{uint32 size;                // 文件大小,type 为文件夹时该字段为 0uint32 type;                // 文件类型uint8 filename[32];         // 文件名称uint32 blocks;              // 占据磁盘块个数uint32 direct[12];          // 直接磁盘块uint32 indirect;            // 间接磁盘块
} Inode;
  • type 字段表示这个 Inode 所代表的文件的类型,可用取值有 TYPE_FILE (普通文件) 或 TYPE_DIR (文件夹)。当 type 取 TYPE_DIR 时,size 字段为 0。并且直接磁盘块和间接磁盘块都是指向存储在该文件夹下的文件的 Inode。当 type 取 TYPE_FILE 时,磁盘块指向实际的数据块。
  • 注意,当 Inode 类型为文件夹时,direct[0] direct[1] 分别存储指向当前和指向上一级文件夹的 Inode。
  • 关于直接磁盘块和间接磁盘块,示意图如下:

在这里插入图片描述

  • 每个 Inode 块中有一个长度为 12 的 direct 数据,如果 blocks 字段小于等于 12,可以直接使用该数组存储磁盘块号,否则,由 indirect 字段指向一个 Indirect Block,在该磁盘块中可以存储更多的磁盘块号。
  • Inode 块的剩余空间也由 0 填充。
    4、相关数据
  • 一个 Freemap 块可以表示 32K 个磁盘块的空闲状况(一个磁盘块为 4K 大小,共 32K 个bit位)
  • 一个文件夹下,除去 “.”“..” 以外,最多可以存储 1034 个文件或文件夹(12+1024)
  • 单个普通文件大小最大为 4.04 MB(1034*4K=4.04M
8.2 打包镜像

1、多用户程序编译

  • 我们将 user 文件夹下的文件分成两类,一类是依赖,一类是用户编写的应用程序
UPROSBASE =                    \$U/entry.o                \$U/malloc.o                \$U/io.o                    UPROS =                        \hello                    \hello2
  • 这里定义了两个应用程序,hello2 仅仅是把 hello 复制了一遍,并修改了输出。
  • 接着我们就需要遍历 UPROS,来将每一个用户程序都编译成一个可执行文件,而不是把它们全都链接成一个文件。
User: $(subst .c,.o,$(wildcard $U/*.c))mkdir -p rootfs/binfor file in $(UPROS); do                                            \$(LD) $(LDFLAGS) -o rootfs/bin/$$file $(UPROSBASE) $U/$$file.o;    \done
  • User 任务首先会创建文件夹 rootfs/bin,所有编译出的可执行文件都会被放置在这个文件夹下。之后就是将每个应用程序都和其依赖链接起来,链接成一个可执行文件。
    2、经过以上过程后,所有的用户程序都会被编译成可执行文件放在 rootfs/bin 文件夹下。现在我们要以 rootfs 作为文件系统的根目录,将其打包成一个 SimpleFS 格式的镜像。
  • 首先要定义文件系统的一些结构:
// mkfs/simplefs.h#ifndef _SIMPLEFS_H
#define _SIMPLEFS_H#include "types.h"#define BLOCK_SIZE  4096
#define MAGIC_NUM   0x4D534653U // MSFStypedef struct {uint32 magic;               // 魔数uint32 blocks;              // 总磁盘块数uint32 unusedBlocks;        // 未使用的磁盘块数uint32 freemapBlocks;       // freemap 块数uint8 info[32];             // 其他信息
} SuperBlock;#define TYPE_FILE   0
#define TYPE_DIR    1typedef struct
{uint32 size;                // 文件大小,type 为文件夹时该字段为0uint32 type;                // 文件类型uint8 filename[32];         // 文件名称uint32 blocks;              // 占据磁盘块个数uint32 direct[12];          // 直接磁盘块uint32 indirect;            // 间接磁盘块
} Inode;#endif
  • 接着就是按部就班地填充各个块了。我们定义的一个 Image 字节数组,它的大小和文件系统一致,所有的块都首先写入 Image 中,最后再将 Image 保存成文件。
// mkfs/mksfs.c/** mksfs.c 用于将一个文件夹作为根目录打包成一个 SimpleFS 镜像文件* * SimpleFS 镜像组成如下:* +-------+------+     +------+------+------+     +------+* | Super | Free | ... | Free | Root |Other | ... |Other |* | Block | Map  |     | Map  |Inode |Inode |     |Inode |* +-------+------+     +------+------+------+     +------+*/#include "types.h"
#include "simplefs.h"
// 标准库
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>// 总块数 256 块,大小为 1M
#define BLOCK_NUM       256
// Freemap 块的个数
#define FREEMAP_NUM     1// 定义 Image 字节数组,它的大小和文件系统一致
// 所有的块都首先写入 Image 中,最后再将 Image 保存成文件
// 最终的镜像数据
char Image[BLOCK_SIZE * BLOCK_NUM];// 临时的 freemap,最后需要写入 Image,此时一个 char 代表一块
char freemap[BLOCK_NUM];
uint32 freenum = BLOCK_NUM;// 被打包的文件夹名称
char *rootdir = "rootfs";void walk(char *dirName, Inode *nowInode, uint32 nowInodeNum);
uint64 getBlockAddr(int blockNum);
int getFreeBlock();
void copyInodeToBlock(int blockNum, Inode *in);// 初始化各个块,并写入 Image 中
void
main()
{// 最开始的几块分别是 超级块,freemap 块 和 root 文件夹所在的 inodefreemap[0] = 1;int i;// 设置 freemap 块为已占用for(i = 0; i < FREEMAP_NUM; i ++) freemap[1+i] = 1;freemap[FREEMAP_NUM + 1] = 1;   // 设置根目录块为已占用freenum -= (FREEMAP_NUM + 2);   // 更新空闲块数量// 填充 superblock 信息SuperBlock spBlock;spBlock.magic = MAGIC_NUM;              // 魔数spBlock.blocks = BLOCK_NUM;             // 文件系统总块数spBlock.freemapBlocks = FREEMAP_NUM;    // 空闲块数char *info = "SimpleFS By JokerDebug";  // 文件系统信息for(i = 0; i < strlen(info); i ++) {spBlock.info[i] = info[i];}spBlock.info[i] = '\0';// 设置根 inodeInode rootInode;rootInode.size = 0;         // 目录文件大小为0rootInode.type = TYPE_DIR;  // 文件类型为目录rootInode.filename[0] = '/'; rootInode.filename[1] = '\0';  // 根目录文件名// 递归遍历根文件夹,并设置和填充数据// 递归遍历文件夹,为每个文件和文件夹创建 Inode 并填充信息walk(rootdir, &rootInode, FREEMAP_NUM+1);spBlock.unusedBlocks = freenum; // 设置超级块中未使用的块数// 将超级块写入 Imagechar *ptr = (char *)getBlockAddr(0), *src = (char *)&spBlock;for(i = 0; i < sizeof(spBlock); i ++) {ptr[i] = src[i];}// 将 freemap 写入 Imageptr = (char *)getBlockAddr(1);for(i = 0; i < BLOCK_NUM/8; i ++) {char c = 0;int j;for(j = 0; j < 8; j ++) {if(freemap[i*8+j]) {c |= (1 << j);}}*ptr = c;ptr ++;}// 将 rootInode 写入 ImagecopyInodeToBlock(FREEMAP_NUM+1, &rootInode);// 将 Image 写到磁盘上FILE *img = fopen("fs.img", "w+b");fwrite(Image, sizeof(Image), 1, img);fflush(img); fclose(img);
}
  • walk() 函数用于递归遍历文件夹,为每个文件和文件夹创建 Inode 并填充信息
// mkfs/mksfs.c/* 根据块号获取 Image 中的块的起始地址 */
uint64
getBlockAddr(int blockNum) {void *addr = (void *)Image;         // Image起始地址addr += (blockNum * BLOCK_SIZE);    // 加上偏移return (uint64)addr;
}// 递归遍历文件夹,为每个文件和文件夹创建 Inode 并填充信息,将文件的 inode 写入磁盘块
// dirName 当前文件夹名,nowInode 为当前文件夹的 Inode,nowInodeNum 为其 Inode 号
void
walk(char *dirName, Inode *nowInode, uint32 nowInodeNum)
{// 打开当前文件夹DIR *dp = opendir(dirName);struct dirent *dirp;// 文件夹下第一个文件为其自己nowInode->direct[0] = nowInodeNum;if(!strcmp(dirName, rootdir)) {     // 判断是否为根目录// 若在根目录,则无上一级,上一级文件夹也为其自己nowInode->direct[1] = nowInodeNum;}// 下一个文件的序号int emptyIndex = 2;     // 初始化空闲位置索引,从第 2 个位置开始// 遍历当前文件夹下所有文件,创建 Inode 并填充信息while((dirp = readdir(dp))) {if(!strcmp(dirp->d_name, ".") || !strcmp(dirp->d_name, "..")) {// 跳过 . 和 ..continue;}int blockNum;if(dirp->d_type == DT_DIR) {// 文件夹处理,递归遍历Inode dinode;dinode.size = 0;            // 设置文件夹 inode 大小为 0dinode.type = TYPE_DIR;     // 设置文件夹类型int i;for(i = 0; i < strlen(dirp->d_name); i ++) {dinode.filename[i] = dirp->d_name[i];   // 将文件夹名称复制到 inode 的 filename}dinode.filename[i] = '\0';blockNum = getFreeBlock();          // 获取一个空闲的块来存储文件夹// 文件夹的前两个文件分别为 . 和 ..dinode.direct[0] = blockNum;        // 当前文件夹dinode.direct[1] = nowInodeNum;     // 父文件夹char *tmp = (char *)malloc(strlen(dirName) + strlen(dirp->d_name) + 1);sprintf(tmp, "%s/%s", dirName, dirp->d_name);   // 拼接文件夹的完整路径walk(tmp, &dinode, blockNum);                   // 递归处理子文件夹copyInodeToBlock(blockNum, &dinode);            // 将文件夹的 inode 写入磁盘块} else if(dirp->d_type == DT_REG) {// 普通文件处理Inode finode;finode.type = TYPE_FILE;int i;for(i = 0; i < strlen(dirp->d_name); i ++) {finode.filename[i] = dirp->d_name[i];       // 将文件名称复制到 inode 的 filename}finode.filename[i] = '\0';char *tmp = (char *)malloc(strlen(dirName) + strlen(dirp->d_name) + 1);sprintf(tmp, "%s/%s", dirName, dirp->d_name);   // 拼接文件的完整路径// 获取文件信息struct stat buf;stat(tmp, &buf);            // 获取文件的状态信息finode.size = buf.st_size;  // 设置文件大小finode.blocks = (finode.size - 1) / BLOCK_SIZE + 1; // 计算文件所占的块数blockNum = getFreeBlock();  // 获取一个空闲的块来存储文件数据// 将文件数据复制到对应的块uint32 l = finode.size;         // 剩余未拷贝的大小int blockIndex = 0;FILE *fp = fopen(tmp, "rb");    // 以二进制读取文件while(l) {int ffb = getFreeBlock();                   // 获取空闲块char *buffer = (char *)getBlockAddr(ffb);   // 获取块的地址size_t size;if(l > BLOCK_SIZE) size = BLOCK_SIZE;       // 如果剩余数据大于块大小,则取块大小else size = l;                              // 否则剩余的大小为实际大小fread(buffer, size, 1, fp);                 // 将文件内容读取到 bufferl -= size;                                  // 减少剩余大小if(blockIndex < 12) {finode.direct[blockIndex] = ffb;        // 前 12 块直接存储数据} else {// 12 个间接块均已使用if(finode.indirect == 0) {finode.indirect = getFreeBlock();   // 如果间接块未分配,分配一个空闲块}uint32 *inaddr = (uint32 *)getBlockAddr(finode.indirect);   // 获取间接块的地址inaddr[blockIndex - 12] = ffb;          // 将块号保存到间接块中(共可存储1024个块,减去12的直接块偏移)}blockIndex ++;                              // 增加块索引}fclose(fp);                                     // 关闭文件copyInodeToBlock(blockNum, &finode);            // 将文件的 inode 写入磁盘块} else {continue;   // 跳过其他类型的文件}// 更新当前文件夹 nowInode 的信息if(emptyIndex < 12) {// 如果直接块未满,直接将块号存储到 nowInode 的 direct 数组中nowInode->direct[emptyIndex] = blockNum;} else {if(nowInode->indirect == 0) {nowInode->indirect = getFreeBlock();    // 如果间接块未分配,分配一个空闲块}uint32 *inaddr = (uint32 *)getBlockAddr(nowInode->indirect);    // 获取间接块的地址inaddr[emptyIndex - 12] = blockNum;         // 将块号存储到间接块中}emptyIndex ++;              // 增加空闲位置索引}closedir(dp);                   // 关闭当前文件夹nowInode->blocks = emptyIndex;  // 更新当前文件夹所占的块数
}
  • getFreeBlock() 函数用于从 freemap 中找到一个空闲的块,将其标记为占用并返回其块号
// mkfs/mksfs.c// 从 freemap 中找到一个空闲的块,将其标记为占用并返回其块号
int getFreeBlock() {int i;// 遍历freemap标志位for(i = 0; i < BLOCK_NUM; i ++) {if(!freemap[i]) {freemap[i] = 1;     // 标记为占用freenum --;         // 更新空闲块数return i;}}printf("get free block failed!\n");exit(1);
}

3、在 Makefile 中新建一个 target,用来编译这个打包工具。此时就不需要使用 RISCV 工具链了(riscv64-linux-gnu-objdump),因为我们需要在当前机器上完成打包过程,所以直接使用 gcc 即可。编译完成后,在 User 运行的最后执行这个打包工具。

mksfs:gcc mkfs/mksfs.c -o mksfsUser: mksfs $(subst .c,.o,$(wildcard $U/*.c))mkdir -p rootfs/binfor file in $(UPROS); do                                            \$(LD) $(LDFLAGS) -o rootfs/bin/$$file $(UPROSBASE) $U/$$file.o;    \done./mksfs
  • 打包后的结果为 fs.img 文件,大小为 1M,和我们在代码中定义的大小相同。
    4、合并内核
  • 这里为了简化过程,依旧是将文件系统的内容直接链接到 .data 段,这样做可以更加注重文件系统相关的操作,而不是去适配 QEMU 的驱动。
  • 只需要修改之前用来链接 ELF 文件的汇编即可。
// kernel/linkFS.asm# 将文件系统链接到 .data 段.section .data.global _fs_img_start.global _fs_img_end
_fs_img_start:.incbin "fs.img"
_fs_img_end:
8.3 内核文件驱动

1、目前的需求是,可以根据一个路径,从文件系统中找到这个文件的 Inode,并且读取它的所有数据到一个字节数组中。基于这个需求,我们先定义一些函数,创建文件等功能可以后续再支持。

  • 文件系统初始化:找到 root inode 即可。
// kernel/fs.cInode *ROOT_INODE;      // 声明一个指针 ROOT_INODE,指向根目录的 Inode 结构
char *FREEMAP;          // 声明一个指针 FREEMAP,指向文件系统的 freemap// 初始化文件系统(只需要找到 root inode 即可)
void
initFs()
{// 获取 freemap 块地址FREEMAP = (char *)getBlockAddr(1);      // 获取超级块的起始地址,_fs_img_start 是文件系统镜像的起始位置(在 linkUser 中定义)SuperBlock* spBlock = (SuperBlock *)_fs_img_start;  // 根目录的 Inode 紧接在 freemap 块之后ROOT_INODE = (Inode *)getBlockAddr(spBlock->freemapBlocks + 1);
}
  • 因为线程初始化要从文件系统中读取 ELF 文件,所以文件系统的初始化需要在线程之前
// kernel/main.cvoid main()
{extern void initInterrupt();    initInterrupt();    // 设置中断处理程序入口 和 模式extern void initTimer();        initTimer();        // 时钟中断初始化extern void initMemory();       initMemory();       // 初始化 页分配 和 动态内存分配extern void mapKernel();        mapKernel();        // 内核重映射,三级页表机制extern void initFs();           initFs();           // 初始化文件系统extern void initThread();       initThread();       // 初始化线程管理extern void runCPU();           runCPU();           // 切换到 idle 调度线程,表示正式由 CPU 进行线程管理和调度while(1) {}
}
  • lookup() 函数将传入的 Inode 作为当前目录,从当前目录下根据路径查找文件。例如,若当前目录为 /usr,传入 ./hellohello 甚至 ../usr/hello 都可以找到可执行文件 hello。如果传入的路径以 / 开头,函数会忽略当前路径而从根目录下开始查找。如果传入的 Inode 为 null,函数也会从根目录开始查找。
  • 函数根据路径分割出需要在当前目录下查找的文件或文件夹名称,并递归调用 lookup 函数进一步搜索。
// kernel/fs.c/** 传入的 Inode 作为当前目录,从当前目录下根据路径查找文件* 若当前目录为 /usr,传入 ./hello 和 hello 甚至 ../usr/hello 都可以找到可执行文件 hello* 如果传入的路径以 / 开头,函数会忽略当前路径而从根目录下开始查找* 如果传入的 Inode 为 null,函数也会从根目录开始查找 
*/
Inode *
lookup(Inode *node, char *filename)
{// 如果文件名以 '/' 开头,表示从根目录开始查找if(filename[0] == '/') {node = ROOT_INODE;      // 将当前节点指向根目录的 Inodefilename ++;            // 跳过 '/' 字符}// 如果当前节点为 NULL,则设为根目录 Inodeif(node == 0) node = ROOT_INODE;// 如果文件名为空(表示已找到目标 Inode),返回当前节点if(*filename == '\0') return node;// 如果当前节点不是目录类型,则返回 NULLif(node->type != TYPE_DIR) return 0;// 创建一个字符串变量用来存储目标文件名char cTarget[strlen(filename) + 1];int i = 0;// 从文件名中提取出一个部分(直到遇到 '/' 或字符串结束)while (*filename != '/' && *filename != '\0') {cTarget[i] = *filename;filename ++;i ++;}cTarget[i] = '\0';// 如果文件名后面还有 '/',则跳过它if(*filename == '/') filename ++;// 如果目标文件名是 ".",表示当前目录,递归调用 lookup 查找下一个部分if(!strcmp(".", cTarget)) {return lookup(node, filename);}// 如果目标文件名是 "..",表示父目录,递归查找父目录if(!strcmp("..", cTarget)) {Inode *upLevel = (Inode *)getBlockAddr(node->direct[1]);    // 获取父目录的 Inodereturn lookup(upLevel, filename);       // 递归查找父目录}// 当前节点的块数int blockNum = node->blocks;// 如果块数小于等于 12,则直接在 direct 数组中查找文件if(blockNum <= 12) {for(i = 2; i < blockNum; i ++) {Inode *candidate = (Inode *)getBlockAddr(node->direct[i]);  // 获取当前块的 Inode// 如果文件名匹配,则递归查找该文件if(!strcmp((char *)candidate->filename, cTarget)) {return lookup(candidate, filename);}}return 0;   // 如果没有找到匹配的文件,则返回 NULL} else {// 如果块数大于 12,先在 direct 数组中查找for(i = 2; i < 12; i ++) {Inode *candidate = (Inode *)getBlockAddr(node->direct[i]);  // 获取当前块的 Inode// 如果文件名匹配,则递归查找该文件if(!strcmp((char *)candidate->filename, cTarget)) {return lookup(candidate, filename);}}// 如果文件仍然没有找到,则在间接块(indirect)中查找uint32 *indirect = (uint32 *)getBlockAddr(node->indirect);      // 获取间接块的地址for(i = 12; i < blockNum; i ++) {Inode *candidate = (Inode *)getBlockAddr(indirect[i-12]);   // 获取间接块中的 Inode// 如果文件名匹配,则递归查找该文件if(!strcmp((char *)candidate->filename, cTarget)) {return lookup(candidate, filename);}}return 0;   // 如果没有找到匹配的文件,则返回 NULL}
}
  • readall() 函数传入一个 Inode 和一个字节数组,Inode 应当为文件类型的 Inode。这个函数将该 Inode 所代表的文件数据全部拷贝到字节数组中
/* 读取一个表示文件的 Inode 的所有字节到 buf 中 */
void
readall(Inode *node, char *buf) {// 检查 Inode 类型是否为文件,如果不是文件则触发 panicif(node->type != TYPE_FILE) {panic("Cannot read a directory!\n");}// 获取文件的大小和文件占用的块数int l = node->size, b = node->blocks;// 如果文件占用的块数小于或等于 12,则直接在 direct 数组中读取数据if(b <= 12) {int i;for(i = 0; i < b; i ++) {// 获取当前块的地址char *src = (char *)getBlockAddr(node->direct[i]);// 拷贝大小判断,大于一页取4096int copySize = l >= 4096 ? 4096 : l;// 将数据从块中复制到 buf 中copyByteToBuf(src, buf, copySize);// 更新 buf 指针,指向下一个写入的位置buf += copySize;// 更新剩余大小l -= copySize;}} else {// 如果文件占用的块数大于 12,先处理前 12 块int i;// 同上for(i = 0; i < 12; i ++) {char *src = (char *)getBlockAddr(node->direct[i]);int copySize = l >= 4096 ? 4096 : l;copyByteToBuf(src, buf, copySize);buf += copySize;l -= copySize;}// 获取间接块地址uint32 *indirect = (uint32 *)getBlockAddr(node->indirect);// 处理所有间接块数据for(i = 0; i < b-12; i ++) {char *src = (char *)getBlockAddr(indirect[i]);int copySize = l >= 4096 ? 4096 : l;copyByteToBuf(src, buf, copySize);buf += copySize;l -= copySize;}}
}
8.4 文件系统测试

1、我们完成了文件系统的简单驱动,现在就可以从文件系统中读取 ELF 文件了!

// kernel/thread.c// 初始化线程
void initThread()
{// 1.创建调度函数实现Scheduler s = {schedulerInit,schedulerPush,schedulerPop,schedulerTick,schedulerExit};s.init(); // 初始化调度器// 2.创建线程池ThreadPool pool = newThreadPool(s);// 3.构建idle调度线程Thread idle = newKernelThread((usize)idleMain);// 4.初始化CPU调度器initCPU(idle, pool);// 5.构造线程并添加到CPU中usize i;for (i = 0; i < 5; i++){Thread t = newKernelThread((usize)helloThread); // 构造新内核线程usize args[8];args[0] = i;appendArguments(&t, args); // 为线程传入初始化参数// 6.启动addToCPU(t); // 将线程添加到调度队列中}// 从文件系统中读取 elf 文件Inode *helloInode = lookup(0, "/bin/hello");    // 查找文件inodechar *buf = kalloc(helloInode->size);           // 分配内存readall(helloInode, buf);                       // 读取文件所有数据到bufThread t = newUserThread(buf);kfree(buf);// 创建一个用户线程并添加到 CPU// extern void _user_img_start(); // linkUser.asm中定义用户程序位置// Thread t = newUserThread((char *)_user_img_start);addToCPU(t);printf("***** init thread *****\n");
}
  • 首先获取到 ELF 文件的 Inode,根据其大小分配一个 buffer,再将文件的内容全部读取到 buffer 里。再次运行,运行结果和上一章一致。

友情提示,在kernel文件夹下记得新增如下文件并引用:

// kernel/fs.h#ifndef _SIMPLEFS_H
#define _SIMPLEFS_H#include "types.h"#define BLOCK_SIZE  4096
#define MAGIC_NUM   0x4D534653U // MSFStypedef struct {uint32 magic;               // 魔数uint32 blocks;              // 总磁盘块数uint32 unusedBlocks;        // 未使用的磁盘块数uint32 freemapBlocks;       // freemap 块数uint8 info[32];             // 其他信息
} SuperBlock;#define TYPE_FILE   0
#define TYPE_DIR    1typedef struct
{uint32 size;                // 文件大小,type 为文件夹时该字段为0uint32 type;                // 文件类型uint8 filename[32];         // 文件名称uint32 blocks;              // 占据磁盘块个数uint32 direct[12];          // 直接磁盘块uint32 indirect;            // 间接磁盘块
} Inode;Inode *lookup(Inode *node, char *filename);
void readall(Inode *node, char *buf);
// void ls(Inode *node);
// char *getInodePath(Inode *inode, char path[256]);#endif
  • 同时也记得新增string.c的字符串操作
// kernel/string.c// 字符串长度
int
strlen(char *str)
{int num = 0;while (*str != '\0') {num ++;str ++;}return num;
}// 字符串对比
int
strcmp(char *str1, char *str2)
{if(strlen(str1) != strlen(str2)) return 1;int i, len = strlen(str1);for(i = 0; i < len; i ++) {if(str1[i] != str2[i]) return 1;}return 0;
}
http://www.lryc.cn/news/611559.html

相关文章:

  • 机器学习第六课之贝叶斯算法
  • 《第五篇》基于RapidOCR的图片和PDF文档加载器实现详解
  • 新能源汽车热管理系统核心零部件及工作原理详解
  • apache-tomcat-11.0.9安装及环境变量配置
  • 【算法训练营Day21】回溯算法part3
  • Redis的分布式序列号生成器原理
  • 【C++详解】STL-set和map的介绍和使用样例、pair类型介绍、序列式容器和关联式容器
  • 部署 Zabbix 企业级分布式监控笔记
  • 无人机开发分享——基于行为树的无人机集群机载自主决策算法框架搭建及开发
  • 分布式微服务--GateWay(1)
  • 3479. 水果成篮 III
  • Minio 高性能分布式对象存储
  • 分布式光伏气象站:安装与维护
  • 【论文分析】【Agent】SEW: Self-Evolving Agentic Workflows for Automated Code Generatio
  • 支持多网络协议的测试工具(postman被无视版)
  • 【概念学习】早期神经网络
  • ORACLE 19C建库时卡在46%、36%
  • Godot ------ 初级人物血条制作01
  • OpenAI开源大模型gpt-oss系列深度解析:从120B生产级到20B桌面级应用指南
  • Unity3D中的Controller:深入解析动画控制器的核心概念与应用
  • 【数据库】Oracle学习笔记整理之一:ORACLE的核心组成部分
  • 【YOLOv8改进 - C2f融合】C2f融合DBlock(Decoder Block):解码器块,去模糊和提升图像清晰度
  • 微信小程序最大层级跳转问题
  • [Oracle] SIGN()函数
  • RabbitMQ 全面指南:从基础概念到高级特性实现
  • Unix/Linux 系统编程中用于管理信号处理行为的核心概念或模型
  • 外观模式(Facade Pattern)及其应用场景
  • Leetcode-3488距离最小相等元素查询
  • 系统的缓存(buff/cache)是如何影响系统性能的?
  • 第五十篇:AI画家的“神经中枢”:ComfyUI的推理路径与缓存逻辑深度解析