欢迎光临企业网站推广全程服务

企业网站推广

10年网站推广经验,之前与我们合作的企业代理商已经推广网站推广开始赚钱了,超越同行不是梦

清华大学操作系统课程 ucore Lab8 文件系统 实验报告

作者:jcmp      发布时间:2021-03-20      浏览量:0
一、操作系统 Lab8 文件系统 实验报

一、操作系统 Lab8 文件系统 实验报告

课程信息所在网址: https://github.com/chyyuu/os_course_info。

二、实验目的

三、实验内容

四、基本练习

五、练习0:填写已有实验

在本练习中将LAB1/2/3/4/5/6/7的实验内容移植到了LAB8的实验框架内,由于手动进行内容移植比较烦杂,因此考虑使用diff和patch工具进行自动化的移植,具体使用的命令如下所示:(对于patch工具进行合并的时候产生冲突的少部分内容,则使用*.rej, *.orig文件来手动解决冲突问题)。

diff -r -u -P lab7_origin lab7 > lab7.patchcd lab8patch -p1 -u < ../lab7.patch。

练习1:完成读文件操作的实现(需要编码)

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

六、设计实现

七、问题回答

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

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

八、设计实现

static intload_icode(int fd, int argc, char **kargv) { if (current->mm != NULL) { // 判断当前进程的mm是否已经被释放掉了 panic("load_icode: current->mm must be empty.\n"); } int ret = -E_NO_MEM; struct mm_struct *mm; // (1) create a new mm for current process if ((mm = mm_create()) == NULL) { // 为进程创建一个新的mm goto bad_mm; } // (2) create a new PDT if ((ret = setup_pgdir(mm)) != 0) { // 进行页表项的设置 goto bad_pgdir_cleanup_mm; } // (3) copy TEXT/DATA/BSS section // (3.1) resolve elf header struct elfhdr elf, *elfp = &elf; off_t offset = 0; load_icode_read(fd, (void *) elfp, sizeof(struct elfhdr), offset); // 从磁盘上读取出ELF可执行文件的elf-header offset += sizeof(struct elfhdr); if (elfp->e_magic != ELF_MAGIC) { // 判断该ELF文件是否合法 ret = -E_INVAL_ELF; goto bad_elf_cleanup_pgdir; } struct proghdr ph, *php = &ph; uint32_t vm_flags, perm; struct Page *page; for (int i = 0; i < elfp->e_phnum; ++ i) { // 根据elf-header中的信息,找到每一个program header // (3.2) resolve prog header load_icode_read(fd, (void *) php, sizeof(struct proghdr), elfp->e_phoff + i * sizeof(struct proghdr)); // 读取program header if (php->p_type != ELF_PT_LOAD) { continue; } if (php->p_filesz > php->p_memsz) { ret = -E_INVAL_ELF; goto bad_cleanup_mmap; } if (php->p_filesz == 0) { continue; } // (3.3) build vma vm_flags = 0, perm = PTE_U; if (php->p_flags & ELF_PF_X) vm_flags |= VM_EXEC; // 根据ELF文件中的信息,对各个段的权限进行设置 if (php->p_flags & ELF_PF_W) vm_flags |= VM_WRITE; if (php->p_flags & ELF_PF_R) vm_flags |= VM_READ; if (vm_flags & VM_WRITE) perm |= PTE_W; if ((ret = mm_map(mm, php->p_va, php->p_memsz, vm_flags, NULL)) != 0) { // 将这些段的虚拟内存地址设置为合法的 goto bad_cleanup_mmap; } // (3.4) allocate pages for TEXT/DATA sections offset = php->p_offset; size_t off, size; uintptr_t start = php->p_va, end = php->p_va + php->p_filesz, la = ROUNDDOWN(start, PGSIZE); ret = -E_NO_MEM; while (start < end) { if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { // 为TEXT/DATA段逐页分配物理内存空间 goto bad_cleanup_mmap; } off = start - la, size = PGSIZE - off, la += PGSIZE; if (end < la) { size -= la - end; } load_icode_read(fd, page2kva(page) + off, size, offset); // 将磁盘上的TEXT/DATA段读入到分配好的内存空间中去 //memcpy(page2kva(page) + off, page2kva(buff_page), size); start += size, offset += size; } // (3.5) allocate pages for BSS end = php->p_va + php->p_memsz; if (start < la) { // 如果存在BSS段,并且先前的TEXT/DATA段分配的最后一页没有被完全占用,则剩余的部分被BSS段占用,因此进行清零初始化 if (start == end) { continue; } off = start + PGSIZE - la, size = PGSIZE - off; if (end < la) { size -= la - end; } memset(page2kva(page) + off, 0, size); // init all BSS data with 0 start += size; assert((end < la && start == end) || (end >= la && start == la)); } while (start < end) { // 如果BSS段还需要更多的内存空间的话,进一步进行分配 if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { // 为BSS段分配新的物理内存页 goto bad_cleanup_mmap; } off = start - la, size = PGSIZE - off, la += PGSIZE; if (end < la) { size -= la - end; } memset(page2kva(page), 0, size); // 将分配到的空间清零初始化 start += size; } } sysfile_close(fd); // 关闭传入的文件,因为在之后的操作中已经不需要读文件了 // (4) setup user stack vm_flags = VM_READ | VM_WRITE | VM_STACK; // 设置用户栈的权限 if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) { // 将用户栈所在的虚拟内存区域设置为合法的 goto bad_cleanup_mmap; } // setup args uint32_t stacktop = USTACKTOP; uint32_t argsize = 0; for (int j = 0; j < argc; ++ j) { // 确定传入给应用程序的参数具体应当占用多少空间 argsize += (1 + strlen(kargv[j])); // includinng the ending '\0' } argsize = (argsize / sizeof(long) + 1) * sizeof(long); //alignment argsize += (2 + argc) * sizeof(long); stacktop = USTACKTOP - argsize; // 根据参数需要在栈上占用的空间来推算出,传递了参数之后栈顶的位置 uint32_t pagen = argsize / PGSIZE + 4; for (int j = 1; j <= 4; ++ j) { // 首先给栈顶分配四个物理页 assert(pgdir_alloc_page(mm->pgdir, USTACKTOP - PGSIZE * j, PTE_USER) != NULL); } // for convinience, setup mm (5) mm_count_inc(mm); // 切换到用户的内存空间,这样的话后文中在栈上设置参数部分的操作将大大简化,因为具体因为空间不足而导致的分配物理页的操作已经交由page fault处理了,是完全透明的 current->mm = mm; current->cr3 = PADDR(mm->pgdir); lcr3(PADDR(mm->pgdir)); // (6) setup args in user stack uint32_t now_pos = stacktop, argvp; *((uint32_t *) now_pos) = argc; // 设置好argc参数(压入栈) now_pos += 4; *((uint32_t *) now_pos) = argvp = now_pos + 4; // 设置argv数组的位置 now_pos += 4; now_pos += argc * 4; for (int j = 0; j < argc; ++ j) { argsize = strlen(kargv[j]) + 1; // 将argv[j]指向的数据拷贝到用户栈中 memcpy((void *) now_pos, kargv[j], argsize); *((uint32_t *) (argvp + j * 4)) = now_pos; // 设置好用户栈中argv[j]的数值 now_pos += argsize; } // (7) setup tf 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 = stacktop; // 栈顶位置为先前计算过的栈顶位置,注意在C语言的函数调用规范中,栈顶指针指向的位置应该是返回地址而不是第一个参数,这里让栈顶指针指向了第一个参数的原因在于,在中断返回之后,会跳转到ELF可执行程序的入口处,在该入口处会进一步使用call命令调用主函数,这时候也就完成了将Return address入栈的功能,因此这里无需画蛇添足压入返回地址 tf->tf_eip = elfp->e_entry; // 将返回地址设置为用户程序的入口 tf->tf_eflags = 0x2 | FL_IF; // 允许中断,根据IA32的规范,eflags的第1位需要恒为1 ret = 0; out: return ret;bad_cleanup_mmap: // 进行加载失败的一系列清理操作 exit_mmap(mm);bad_elf_cleanup_pgdir: put_pgdir(mm);bad_pgdir_cleanup_mm: mm_destroy(mm);bad_mm: goto out;}

九、问题回答

十、实验结果

最终的实验结果符合预期,并且能够通过make grade脚本的检查,如下图所示:

十一、参考答案分析

十二、练习1

十三、练习2

本实验在练习2中的实现与参考答案的实现大致一致,但是经过仔细的比较,观察到一个细节,参考答案在确定参数的长度的时候使用的函数时strnlen,而本实验中的实现使用了strlen,而后者是不安全的,有可能遭到栈溢出攻击的,因此在这个区别上,参考答案的实现明显优于本实验的实验,这也其实我在完成实际的编程任务的时候需要充分考虑到鲁棒性、安全性等细节,这也是我自己觉得在本次操作系统实验中做得有所欠缺的地方;

十四、实验中涉及的知识点列举

十五、实验中未涉及的知识点列举

在本次实验中未涉及到的知识点列举如下:

十六、实验代码

https://github.com/AmadeusChan/ucore_os_lab/tree/master/lab8。

十七、参考文献