lab 5
总述
这个 lab 要实现一个微内核(micro-kernel)风格的文件系统,整个文件系统跑在一个专门的用户进程(user-space environment)中,而其他进程通过 IPC 请求来操作这个文件系统
结构介绍
Sectors and Blocks
-
Sector
磁盘读写操作的最小单元,现在常见的多为 512 bytes。 -
Block
文件系统管理文件的最小单元,必须是 sector 的整数倍,UNIX xv6 的 block size 是 512 bytes,JOS 的 block size 是 4K(PAGESIZE)。
Superblocks
记录整个文件系统属性(如 block size,disk size 等)的 block,通常位于整个磁盘的第 1 个 block(第 0 个 block 用于 boot)。
Block Bitmap
记录整个磁盘中哪些 block 是空闲的,哪些是已被使用的。在 JOS 中,Bitmap 从第 2 个 block 开始(尾随 superblock)。
File Meta-data
没有 inode
的结构,但是有类似的结构叫做 struct File
,也记录了文件名、文件大小、一级 block 指针、次级 block 指针。没有 inode table,所有 struce File
储存在磁盘上的 directory entry 中。
Directories versus Regular Files
目录和正常文件统一看作正常文件储存。
Summary
整个结构和 CSE 实现的文件系统大体一致,在 inode 的具体实现上有差别。
The File System
Disk Access
-
Exercise 1
这里需要给专门跑 fs 的 environment 赋予访问磁盘的权限。修改env.c/env_create
函数,如果当前进程的type
是ENV_TYPE_FS
,就把它的tf_eflags
上的FL_IOPL_MASK
位置为 1。
The Block Cache
我们不可能把整个磁盘空间都读入内存,所以实现了一种叫做 demanding paging
的形式。即被访问的 page 先产生 pgfault,然后去磁盘中去,江数据存入 block cache,以供读取。
-
Exercise 2
首先补全bc_pgfault
函数。当 pgfault 触发时,申请一个物理页,然后调用ide_read
读取磁盘信息。需要注意的是:
1)addr 可能没有与 block size 对齐,需要 ROUNDDOWN。
2)ide_read 需要传入的参数是按照 sector 来算的,需要经过BLKSECTS
的转换。接下来补全
flush_block
函数。验证传入的 addr 已存在映射并且是 dirty 的,然后调用ide_write
把内容写入磁盘,接着通过调用sys_page_map
来清空这个内存页的信息。
The Block Bitmap
-
Exercise 3
实现allock_block
函数。遍历 bitmap ,调用block_is_free
函数检查是否为空闲,找到的第一块空闲 block 在 bitmap 上标为已用,然后 flush bitmap。如果没有找到空闲 block,返回错误序号。
File Operations
-
Exercise 4
实现file_block_walk
函数和file_get_block
函数。static int file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc) { // LAB 5: Your code here. int r; if(filebno >= NDIRECT + NINDIRECT) return -E_INVAL; else if(filebno < NDIRECT){ if(*ppdiskbno) *ppdiskbno = f->f_direct + filebno; } else{ if(f->f_indirect == 0){ if(alloc == 0) return -E_NOT_FOUND; r = alloc_block(); if(r < 0) return r; memset(diskaddr(r), 0, BLKSIZE); flush_block(diskaddr(r)); f->f_indirect = r; } if(ppdiskbno) *ppdiskbno = (uint32_t *)diskaddr(f->f_indirect) + (filebno - NDIRECT); } return 0; } int file_get_block(struct File *f, uint32_t filebno, char **blk) { // LAB 5: Your code here. uint32_t * pdiskno; int r = file_block_walk(f,filebno,&pdiskno,1); if(r < 0) return r; if(!*pdiskno){ r = alloc_block(); if(r < 0) return r; *pdiskno = r; } *blk = diskaddr(*pdiskno); return 0; }
Client Server Access
基于 IPC 实现 C/S 风格的文件系统的调用。
Client-Side File Operations
这部分要实现客户端的 open
操作,通过 IPC 发送到文件系统的 env 中来打开文件。这里提到了 UNIX 中 everything is a file 的思想。
-
Exercise 7
实现如下:// LAB 5: Your code here. struct Fd *fd; int r; if(strlen(path) >= MAXPATHLEN) return -E_BAD_PATH; r = fd_alloc(&fd); if(r < 0) return r; strcpy(fsipcbuf.open.req_path, path); fsipcbuf.open.req_omode = mode; r = fsipc(FSREQ_OPEN, fd); if(r < 0){ fd_close(fd, 0); return r; } return fd2num(fd);
-
Exercise 8
实现如下:struct Env *e; int r = envid2env(envid, &e, 1); if(r < 0) return r; e->env_tf = *tf; e->env_tf.tf_cs |= 3; e->env_tf.tf_eflags |= FL_IF; return 0;