ucore_lab8 实验报告

实验目的

通过完成本次实验,希望能达到以下目标

  • 了解基本的文件系统系统调用的实现方法;
  • 了解一个基于索引节点组织方式的Simple FS文件系统的设计与实现;
  • 了解文件系统抽象层-VFS的设计与实现;

某操作系统中建立了一个基于索引节点(index node)结构的文件系统very simple file system, 简称vsfs。

vsfs的用户操作包括(以函数形式表示):

  • mkdir(“str”) - 创建一个新目录,目录名称为”str”

  • creat(“str”) - 创建一个空文件,空文件名称为“str”

  • fd=open(“str”), write(fd), close(fd) – 打开文件”str”,会返回一个整型数fd, 然后对文件写一个buffer,注意常规文件的最大size是一个data block,所以第二次写(写文件的语义是在上次写的位置后再写一个data block)会报错(文件大小满了)。或者如果data block也满了,也会报错。

  • link(“a1”,”b1”) - 对文件”a1”创建一个硬链接(hard link)文件”b1”

  • unlink(“b1”) - 对文件“b1”取消一个硬链接,如果文件的链接数为0,则删除此文件

vsfs在硬盘上的布局:

  • superblock : 可用inode数量,可用data block数量

  • inode bitmap : inode的分配图(基于bitmap)

  • inodes : inode的存储区域

  • data bitmap : data block的分配图(基于bitmap)

  • data : data block的存储区域

vsfs的关键数据结构:

  • inode数据结构:

  • inode : 包含3个fields, 用 list 表示

  • file type: f -> 常规文件:regular file, d -> 目录文件:directory

  • data block addr of file content: -1 -> file is empty

  • reference count: file/directory的引用计数,注意directory的引用计数是指在此目录中的inode的个数

注意:比如,刚创建的一个空文件inode: [f a:-1 r:1], 一个有1个硬链接的文件inode: [f a:10 r:2]

  • 数据块内容结构:

  • 一般文件的内容的表示:只是包含单个字符的list,即占一个data block,比如[‘a’], [‘b’] …

  • 目录内容的表示: 多个两元组(name, inode_number)形成的list,比如, 根目录[(.,0) (…,0)], 或者包含了一个’f’文件的根目录[(.,0) (…,0) (f,1)] 。

注意:

  • 一个目录的目录项的个数是有限的。 block.maxUsed = 32

  • data block的个数是有限的,为 fs.numData

  • inode的个数是有限的,为 fs.numInodes

完整文件系统的例子:

  • fs.ibitmap:      inode bitmap 11110000

  • fs.inodes:       [d a:0 r:5] [f a:1 r:1] [f a:-1 r:1] [d a:2 r:2] [] …

  • fs.dbitmap:    data bitmap 11100000

  • fs.data:           [(.,0) (…,0) (y,1) (z,2) (x,3)] [u] [(.,3) (…,0)] [] …

表明: 此文件系统有8个inode空间, 8个data blocks. 其中,根目录包含5个目录项,“.”,“…”,“y”,“z”,“x”。 而“y”是常规文件,并有文件内容,包含一个data block,文件内容为“u”。“z”是一个空的常规文件。“x”是一个目录文件,是空目录。

如果vsfs初始状态为:

inode bitmap     10000000

inodes                 [d a:0 r:2] [] [] [] [] [] [] []

data bitmap       10000000

data                     [(.,0) (…,0)] [] [] [] [] [] [] []

请问接下来的连续6个状态变化的对应用户操作是什么?据此回答以下6个问题

练习0:填写已有实验

本实验依赖实验1/2/3/4/5/6/7。请把你做的实验1/2/3/4/5/6/7的代码填入本实验中代码中有“LAB1”/“LAB2”/“LAB3”/“LAB4”/“LAB5”/“LAB6” /“LAB7”的注释相应部分。并确保编译通过。注意:为了能够正确执行lab8的测试应用程序,可能需对已完成的实验1/2/3/4/5/6/7的代码进行进一步改进。

将前面lab的代码合入到lab8中

需要改进的代码:

static struct proc_struct *
alloc_proc(void) {
        // process's file related informaction
        proc->filesp = NULL;
}

int
do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf) {
    // copy the files_struct from current to proc
    if (copy_files(clone_flags, proc) != 0) {
        goto bad_fork_cleanup_fs;
    }
}
练习1: 完成读文件操作的实现(需要编码)

首先了解打开文件的处理流程,然后参考本实验后续的文件读写操作的过程分析,编写在sfs_inode.c中sfs_io_nolock读文件中数据的实现代码。

sfs_io_nolock代码如下:

/*  
 * sfs_io_nolock - Rd/Wr a file content from offset position to offset + length  disk blocks<-->buffer (in memroy)
 * @sfs:      sfs file system
 * @sin:      sfs inode in memory
 * @buf:      the buffer Rd/Wr
 * @offset:   the offset of file
 * @alenp:    the length need to read (is a pointer). and will RETURN the really Rd/Wr lenght
 * @write:    BOOL, 0 read, 1 write
 */
static int
sfs_io_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, void *buf, off_t offset, size_t *alenp, bool write) {
    struct sfs_disk_inode *din = sin->din;
    assert(din->type != SFS_TYPE_DIR);
    off_t endpos = offset + *alenp, blkoff;
    *alenp = 0;
	// calculate the Rd/Wr end position
    if (offset < 0 || offset >= SFS_MAX_FILE_SIZE || offset > endpos) {
        return -E_INVAL;
    }
    if (offset == endpos) {
        return 0;
    }
    if (endpos > SFS_MAX_FILE_SIZE) {
        endpos = SFS_MAX_FILE_SIZE;
    }
    if (!write) {
        if (offset >= din->size) {
            return 0;
        }
        if (endpos > din->size) {
            endpos = din->size;
        }
    }

    int (*sfs_buf_op)(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset);
    int (*sfs_block_op)(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks);
    if (write) {
        sfs_buf_op = sfs_wbuf, sfs_block_op = sfs_wblock;
    }
    else {
        sfs_buf_op = sfs_rbuf, sfs_block_op = sfs_rblock;
    }

    int ret = 0;
    size_t size, alen = 0;
    uint32_t ino;
    // offset = 10110, endpos = 50960
    // SFS_BLKSIZE = 4096
    // blkno = 10110 / 4096 = 2
    // nblks = 50960 / 4096 - 2 = 12 - 2 = 10, 即要读10个blocks
    uint32_t blkno = offset / SFS_BLKSIZE;          // The NO. of Rd/Wr begin block
    uint32_t nblks = endpos / SFS_BLKSIZE - blkno;  // The size of Rd/Wr blocks

    //LAB8:EXERCISE1 YOUR CODE HINT: call sfs_bmap_load_nolock, sfs_rbuf, sfs_rblock,etc. read different kind of blocks in file
	/*
	 * (1) If offset isn't aligned with the first block, Rd/Wr some content from offset to the end of the first block
	 *       NOTICE: useful function: sfs_bmap_load_nolock, sfs_buf_op
	 *               Rd/Wr size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset)
	 * (2) Rd/Wr aligned blocks 
	 *       NOTICE: useful function: sfs_bmap_load_nolock, sfs_block_op
     * (3) If end position isn't aligned with the last block, Rd/Wr some content from begin to the (endpos % SFS_BLKSIZE) of the last block
	 *       NOTICE: useful function: sfs_bmap_load_nolock, sfs_buf_op	
	*/
    // 主要分三种情况,
    // 第一块没有对齐,从偏移的地方开始对齐
    // 中间对齐
    // 最后一块没有对齐

    // 1.第一块没有对齐,先读没有对齐的这一块
    if ((blkoff = (offset % SFS_BLKSIZE)) != 0) {
        size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset);

        // 根据DIR的inode和inode里面block的逻辑索引找到第No.个硬盘块
        if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0) {
            goto out;
        }
        
        if ((ret = sfs_buf_op(sfs, buf, size, ino, blkoff)) != 0) {
        // if ((ret = sfs_buf_op(sfs, buf, size, blkno, offset)) != 0) {
            goto out;
        }

        alen += size;
        buf += size;
        offset += size;
        blkno ++;
        if (nblks == 0)
            goto out;
        else
            nblks --;
    }
    
    // 2.读中间对齐的数据
    if (nblks > 0) {
        if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0)
            goto out;
        if ((ret = sfs_block_op(sfs, buf, ino, nblks)) != 0)
            goto out;
        buf += nblks * SFS_BLKSIZE;
        alen += nblks * SFS_BLKSIZE;
        blkno += nblks;
    }

    // 3.读最后一块没有对齐的数据
    // |-512-|-512-|-512-|-512-|
    // 假如 为1050
    if (size = endpos % SFS_BLKSIZE != 0) {
        if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0) {
            goto out;
        }

        if ((ret = sfs_buf_op(sfs, buf, size, ino, 0)) != 0) {
            goto out;
        }
        
        alen += size;
    }

out:
    *alenp = alen;
    if (offset + alen > sin->din->size) {
        sin->din->size = offset + alen;
        sin->dirty = 1;
    }
    return ret;
}

请在实验报告中给出设计实现”UNIX的PIPE机制“的概要设方案,鼓励给出详细设计方案

简单的可以在在磁盘上专门建立一个临时的文件作为缓冲区,可以通过这一缓冲区进行通信,可以在硬盘上建立一个缓冲文件pipe_temp但是效率太低,实际UNIX是根据FS的规范,在内存中选择一块区域,建立了一个在内存中的虚拟文件系统。

练习2: 完成基于文件系统的执行程序机制的实现(需要编码)

改写proc.c中的load_icode函数和其他相关函数,实现基于文件系统的执行程序机制。执行:make qemu。如果能看看到sh用户程序的执行界面,则基本成功了。如果在sh用户界面上可以执行”ls”,”hello”等其他放置在sfs文件系统中的其他执行程序,则可以认为本实验基本成功。

load_icode函数可以在lab7原先的基础上进行修改,不需要从0开发。参考:uCore实验 - Lab8 | Kiprey’s Blog

代码如下:

// load_icode -  called by sys_exec-->do_execve
static int
load_icode(int fd, int argc, char **kargv) {
    /* LAB8:EXERCISE2 YOUR CODE  HINT:how to load the file with handler fd  in to process's memory? how to setup argc/argv?
     * MACROs or Functions:
     *  mm_create        - create a mm
     *  setup_pgdir      - setup pgdir in mm
     *  load_icode_read  - read raw data content of program file
     *  mm_map           - build new vma
     *  pgdir_alloc_page - allocate new memory for  TEXT/DATA/BSS/stack parts
     *  lcr3             - update Page Directory Addr Register -- CR3
     */
    if (current->mm != NULL)
        panic("load_icode: current->mm must be empty.\n");
    int ret = -E_NO_MEM;
	// (1) create a new mm for current process
    struct mm_struct *mm;
    if ((mm = mm_create()) == NULL)
        goto bad_mm;
    // (2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT
    if (setup_pgdir(mm) != 0) {
        goto bad_pgdir_cleanup_mm;
    }
    // (3) copy TEXT/DATA/BSS parts in binary to memory space of process
    // (3.1) read raw data content in file and resolve elfhdr
    struct elfhdr elf;
    if ((ret = load_icode_read(fd, &elf, sizeof(struct elfhdr), 0)) != 0)
        goto out;
    if (elf.e_magic != ELF_MAGIC) {
        ret = -E_INVAL_ELF;
        goto bad_elf_cleanup_pgdir;
    }
    size_t ph_off = elf.e_phoff;
    size_t ph_end = elf.e_phoff + elf.e_phnum * sizeof(struct proghdr);
    for (; ph_off < ph_end; ph_off += sizeof(struct proghdr)) {
        // (3.2) read raw data content in file and resolve proghdr based on info in elfhdr
        struct proghdr ph;
        if ((ret = load_icode_read(fd, &ph, sizeof(struct proghdr), ph_off)) != 0)
            goto out;
        if (ph.p_type != ELF_PT_LOAD) {
            continue ;
        }
        if (ph.p_filesz > ph.p_memsz) {
            ret = -E_INVAL_ELF;
            goto bad_cleanup_mmap;
        }
        if (ph.p_filesz == 0) {
            continue ;
        }
        // (3.3) call mm_map to build vma related to TEXT/DATA
        uint32_t vm_flags = 0, perm = PTE_U;
        if (ph.p_flags & ELF_PF_X) vm_flags |= VM_EXEC;
        if (ph.p_flags & ELF_PF_W) vm_flags |= VM_WRITE;
        if (ph.p_flags & ELF_PF_R) vm_flags |= VM_READ;
        if (vm_flags & VM_WRITE) perm |= PTE_W;
        if ((ret = mm_map(mm, ph.p_va, ph.p_memsz, vm_flags, NULL)) != 0) {
            goto bad_cleanup_mmap;
        }
        // (3.4) callpgdir_alloc_page to allocate page for TEXT/DATA, read contents in file and copy them into the new allocated pages
        struct Page *page;
        size_t from = ph.p_offset;
        size_t off, size;
        uintptr_t start = ph.p_va, end, la = ROUNDDOWN(start, PGSIZE);
        ret = -E_NO_MEM;
        end = ph.p_va + ph.p_filesz;
        while (start < end) {
            if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL)
                goto bad_cleanup_mmap;
            off = start - la, size = PGSIZE - off, la += PGSIZE;
            if (end < la)
                size -= la - end;
            if ((ret = load_icode_read(fd, page2kva(page) + off, size, from)) != 0)
                goto out;
            start += size, from += size;
        }
        // (3.5) callpgdir_alloc_page to allocate pages for BSS, memset zero in these pages
        end = ph.p_va + ph.p_memsz;
        if (start < la) {
            /* ph->p_memsz == ph->p_filesz */
            if (start == end)
                continue;
            off = start + PGSIZE - la, size = PGSIZE - off;
            if (end < la)
                size -= la - end;
            memset(page2kva(page) + off, 0, size);
            start += size;
            assert((end < la && start == end) || (end >= la && start == la));
        }
        while (start < end) {
            if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
                goto bad_cleanup_mmap;
            }
            off = start - la, size = PGSIZE - off, la += PGSIZE;
            if (end < la) {
                size -= la - end;
            }
            memset(page2kva(page) + off, 0, size);
            start += size;
        }
    }
    // (4) call mm_map to setup user stack, and put parameters into user stack
    uintptr_t esp = USTACKTOP;
    uint32_t vm_flags = VM_READ | VM_WRITE | VM_STACK;
    if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0)
        goto bad_cleanup_mmap;
    char** buffer[EXEC_MAX_ARG_NUM];
    for (int i = 0; i < argc; i++) {
        struct Page *page = pgdir_alloc_page(mm->pgdir, USTACKTOP-(i+1)*PGSIZE , PTE_USER);
        assert(page != NULL);
        uintptr_t kva = page2kva(page);
        memcpy(kva, kargv[i], EXEC_MAX_ARG_LEN + 1);
        esp -= PGSIZE;
        buffer[i] = esp;
    }
    for (int i = argc; i < argc + 4; i++)
        assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-(i+1)*PGSIZE , PTE_USER) != NULL);
    // (5) setup current process's mm, cr3, reset pgidr (using lcr3 MARCO)
    mm_count_inc(mm);
    current->mm = mm;
    current->cr3 = PADDR(mm->pgdir);
    lcr3(PADDR(mm->pgdir));
    // (6) setup uargc and uargv in user stacks
    esp -= sizeof(uintptr_t) * (argc + 1);
    memcpy(esp, buffer, sizeof(uintptr_t) * argc);
    esp -= sizeof(int);
    *((int*) esp) = argc;
    // (7) setup trapframe for user environment
    struct trapframe *tf = current->tf;
    memset(tf, 0, sizeof(struct trapframe));
    tf->tf_cs = USER_CS;
    tf->tf_ds = tf->tf_es = tf->tf_ss = USER_DS;
    tf->tf_esp = esp;
    tf->tf_eip = elf.e_entry;
    tf->tf_eflags = tf->tf_eflags | FL_IF;
    ret = 0;
out:
    return ret;
    // (8) if up steps failed, you should cleanup the env.
bad_cleanup_mmap:
    exit_mmap(mm);
bad_elf_cleanup_pgdir:
    put_pgdir(mm);
bad_pgdir_cleanup_mm:
    mm_destroy(mm);
bad_mm:
    goto out;
}

请在实验报告中给出设计实现基于”UNIX的硬链接和软链接机制“的概要设方案,鼓励给出详细设计方案

硬链接:新建的文件是已经存在的文件的一个别名,当原文件删除时,新建的文件仍然可以使用。与普通文件没什么不同,inode 都指向同一个文件在硬盘中的区块

软链接:也称为符号链接,新建的文件以“路径”的形式来表示另一个文件,和Windows的快捷方式十分相似,新建的软链接可以指向不存在的文件。保存了其代表的文件的绝对路径,是另外一种文件,在硬盘上有独立的区块,访问时替换自身路径。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值