【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,传入./hello
和hello
甚至../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;
}