当前位置: 首页 > 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.dtcms.com/a/318453.html

相关文章:

  • 【Go】新版GORM自动字段映射规则
  • 基于深度学习的医学图像分析:使用Diffusion Models实现医学图像生成
  • word2vector细致分解(CBOW, SKIP_GRAM, 层次soft Max, 负采样)
  • 8月6日星期三今日早报简报微语报早读
  • 机器学习 朴素贝叶斯
  • 园区误报率↓79%!陌讯多模态融合算法在智慧环卫越界识别的工程实践
  • 防火墙及firewall工具详解
  • AI增强的软件测试工具
  • 解决pytorch-lightning1.6.0安装时自动下载最新的pytorch方法
  • 《深入浅出Embedding》这本书
  • 高等数学(工本)----00023 速记宝典
  • <2> Elasticsearch大规模数据迁移实战:从内存暴涨到优化策略
  • 令牌桶限流算法
  • 《动手学深度学习》读书笔记—9.3深度循环神经网络
  • 数字图像处理(冈萨雷斯)第三版:第四章——空间滤波与频域滤波(平滑与锐化)——主要内容和重点
  • SQL166 删除索引
  • 一篇认识synchronized锁
  • JAVA--流程控制语句
  • Android—服务+通知=>前台服务
  • shell基础之EOF的用法
  • 译 | 在 Python 中从头开始构建 Qwen-3 MoE
  • windos安装了python,但是cmd命令行找不到python
  • 012 网络—基础篇
  • 机器学习算法系列专栏:逻辑回归(初学者)
  • flex布局:容器的justify-content属性
  • Python训练Day35
  • Python在生物计算与医疗健康领域的应用(2025深度解析)
  • 局域网内某服务器访问其他服务器虚拟机内相关服务配置
  • 无人机遥控器舵量技术解析
  • 线上Linux服务器的优化设置、系统安全与网络安全策略