ucore操作系统lab8——实验报告

来源:互联网 发布:painter软件设置中文 编辑:程序博客网 时间:2024/05/01 15:10

一、练习一: 完成读文件操作的实现

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


1、打开文件原理:

首先假定用户进程需要打开的文件已经存在在硬盘上。以user/sfs_filetest1.c为例,首先用户进程会调用在main函数中的如下语句:
int fd1 = safe_open("/test/testfile", O_RDWR | O_TRUNC);
从字面上可以看出,如果ucore能够正常查找到这个文件,就会返回一个代表文件的文件描述符fd1,这样在接下来的读写文件过程中,就直接用这样fd1来代表就可以了。那这个打开文件的过程是如何一步一步实现的呢?

①通用文件访问接口层的处理流程

首先进入通用文件访问接口层的处理流程,即进一步调用如下用户态函数: open->sys_open->syscall,从而引起系统调用进入到内核态。到了内核态后,通过中断处理例程,会调用到sys_open内核函数,并进一步调用sysfile_open内核函数。到了这里,需要把位于用户空间的字符串"/test/testfile"拷贝到内核空间中的字符串path中,并进入到文件系统抽象层的处理流程完成进一步的打开文件操作中。

②文件系统抽象层的处理流程

Ⅰ、分配一个空闲的file数据结构变量file在文件系统抽象层的处理中,首先调用的是file_open函数,它要给这个即将打开的文件分配一个file数据结构的变量,这个变量其实是当前进程的打开文件数组current->fs_struct->filemap[]中的一个空闲元素(即还没用于一个打开的文件),而这个元素的索引值就是最终要返回到用户进程并赋值给变量fd1。到了这一步还仅仅是给当前用户进程分配了一个file数据结构的变量,还没有找到对应的文件索引节点。

为此需要进一步调用vfs_open函数来找到path指出的文件所对应的基于inode数据结构的VFS索引节点node。vfs_open函数需要完成两件事情:通过vfs_lookup找到path对应文件的inode;调用vop_open函数打开文件。

Ⅱ、找到文件设备的根目录“/”的索引节点需要注意,这里的vfs_lookup函数是一个针对目录的操作函数,它会调用vop_lookup函数来找到SFS文件系统中的“/test”目录下的“testfile”文件。为此,vfs_lookup函数首先调用get_device函数,并进一步调用vfs_get_bootfs函数(其实调用了)来找到根目录“/”对应的inode。这个inode就是位于vfs.c中的inode变量bootfs_node。这个变量在init_main函数(位于kern/process/proc.c)执行时获得了赋值。

Ⅲ、找到根目录“/”下的“test”子目录对应的索引节点,在找到根目录对应的inode后,通过调用vop_lookup函数来查找“/”和“test”这两层目录下的文件“testfile”所对应的索引节点,如果找到就返回此索引节点。

Ⅳ、把file和node建立联系。完成第3步后,将返回到file_open函数中,通过执行语句“file->node=node;”,就把当前进程的current->fs_struct->filemap[fd](即file所指变量)的成员变量node指针指向了代表“/test/testfile”文件的索引节点node。这时返回fd。经过重重回退,通过系统调用返回,用户态的syscall->sys_open->open->safe_open等用户函数的层层函数返回,最终把把fd赋值给fd1。自此完成了打开文件操作。但这里我们还没有分析第2和第3步是如何进一步调用SFS文件系统提供的函数找位于SFS文件系统上的“/test/testfile”所对应的sfs磁盘inode的过程。下面需要进一步对此进行分析。

③SFS文件系统层的处理流程

这里需要分析文件系统抽象层中没有彻底分析的vop_lookup函数到底做了啥。下面我们来看看。在sfs_inode.c中的sfs_node_dirops变量定义了“.vop_lookup = sfs_lookup”,所以我们重点分析sfs_lookup的实现。

sfs_lookup有三个参数:node,path,node_store。其中node是根目录“/”所对应的inode节点;path是文件“testfile”的绝对路径“/test/testfile”,而node_store是经过查找获得的“testfile”所对应的inode节点。

Sfs_lookup函数以“/”为分割符,从左至右逐一分解path获得各个子目录和最终文件对应的inode节点。在本例中是分解出“test”子目录,并调用sfs_lookup_once函数获得“test”子目录对应的inode节点subnode,然后循环进一步调用sfs_lookup_once查找以“test”子目录下的文件“testfile1”所对应的inode节点。当无法分解path后,就意味着找到了testfile1对应的inode节点,就可顺利返回了。

sfs_lookup_once将调用sfs_dirent_search_nolock函数来查找与路径名匹配的目录项,如果找到目录项,则根据目录项中记录的inode所处的数据块索引值找到路径名对应的SFS磁盘inode,并读入SFS磁盘inode对的内容,创建SFS内存inode。

2、读文件实现

//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*///因为数据不一定与block对其,分成三段读取//读取头部的数据    if ((blkoff = offset % SFS_BLKSIZE) != 0) {        size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset);//找到第一个数据块的大小        if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0) {//先找到内存文件索引对应的block的编号ino            goto out;        }        if ((ret = sfs_buf_op(sfs, buf, size, ino, blkoff)) != 0) {//完成实际的读写操作            goto out;        }        alen += size;        if (nblks == 0) {            goto out;        }        buf += size, blkno ++, nblks --;    }//读取中间部分的数据,一块一块地读    size = SFS_BLKSIZE;    while (nblks != 0) {        if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0) {            goto out;        }        if ((ret = sfs_block_op(sfs, buf, ino, 1)) != 0) {            goto out;        }        alen += size, buf += size, blkno ++, nblks --;    }<span style="white-space:pre"></span>//读取末尾的数据    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;}


二、练习二: 完成基于文件系统的执行程序机制的实现

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

1、实验流程:

①建立内存管理器 

②建立页目录表  

③从硬盘上读取程序内容到内存 

④建立相应的虚拟内存映射表

 ⑤设置好用户栈

 ⑥设置进程的中断帧

2、初始化fs中的进程控制结构

// alloc_proc - alloc a proc_struct and init all fields of proc_structstatic struct proc_struct *alloc_proc(void) {    struct proc_struct *proc = kmalloc(sizeof(struct proc_struct));    if (proc != NULL) {    //LAB4:EXERCISE1 YOUR CODE    /*     * below fields in proc_struct need to be initialized     *       enum proc_state state;                      // Process state     *       int pid;                                    // Process ID     *       int runs;                                   // the running times of Proces     *       uintptr_t kstack;                           // Process kernel stack     *       volatile bool need_resched;                 // bool value: need to be rescheduled to release CPU?     *       struct proc_struct *parent;                 // the parent process     *       struct mm_struct *mm;                       // Process's memory management field     *       struct context context;                     // Switch here to run process     *       struct trapframe *tf;                       // Trap frame for current interrupt     *       uintptr_t cr3;                              // CR3 register: the base addr of Page Directroy Table(PDT)     *       uint32_t flags;                             // Process flag     *       char name[PROC_NAME_LEN + 1];               // Process name     */     //LAB5 YOUR CODE : (update LAB4 steps)    /*     * below fields(add in LAB5) in proc_struct need to be initialized     *       uint32_t wait_state;                        // waiting state     *       struct proc_struct *cptr, *yptr, *optr;     // relations between processes */    //LAB8:EXERCISE2 YOUR CODE HINT:need add some code to init fs in proc_struct, ...        proc->state = PROC_UNINIT;        proc->pid = -1;        proc->runs = 0;        proc->kstack = 0;        proc->need_resched = 0;        proc->parent = NULL;        proc->mm = NULL;        memset(&(proc->context), 0, sizeof(struct context));        proc->tf = NULL;        proc->cr3 = boot_cr3;        proc->flags = 0;        memset(proc->name, 0, PROC_NAME_LEN);        proc->wait_state = 0;        proc->cptr = proc->optr = proc->yptr = NULL;        proc->rq = NULL;        proc->run_link.prev = proc->run_link.next = NULL;        proc->time_slice = 0;        proc->lab6_run_pool.left = proc->lab6_run_pool.right = proc->lab6_run_pool.parent = NULL;        proc->lab6_stride = 0;        proc->lab6_priority = 0;        proc->filesp = NULL;    }    return proc;}

3、load_icode实现

static intload_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     *//* (1) create a new mm for current process     * (2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT     * (3) copy TEXT/DATA/BSS parts in binary to memory space of process     *    (3.1) read raw data content in file and resolve elfhdr     *    (3.2) read raw data content in file and resolve proghdr based on info in elfhdr     *    (3.3) call mm_map to build vma related to TEXT/DATA     *    (3.4) callpgdir_alloc_page to allocate page for TEXT/DATA, read contents in file     *          and copy them into the new allocated pages     *    (3.5) callpgdir_alloc_page to allocate pages for BSS, memset zero in these pages     * (4) call mm_map to setup user stack, and put parameters into user stack     * (5) setup current process's mm, cr3, reset pgidr (using lcr3 MARCO)     * (6) setup uargc and uargv in user stacks     * (7) setup trapframe for user environment     * (8) if up steps failed, you should cleanup the env.     */    assert(argc >= 0 && argc <= EXEC_MAX_ARG_NUM);//(1)建立内存管理器     if (current->mm != NULL) {        panic("load_icode: current->mm must be empty.\n");    }    int ret = -E_NO_MEM;    struct mm_struct *mm;    if ((mm = mm_create()) == NULL) {        goto bad_mm;    }//(2)建立页目录表     if (setup_pgdir(mm) != 0) {        goto bad_pgdir_cleanup_mm;    }    struct Page *page;//(3)从文件加载程序到内存    struct elfhdr __elf, *elf = &__elf;//(3.1)读取elf文件头     if ((ret = load_icode_read(fd, elf, sizeof(struct elfhdr), 0)) != 0) {        goto bad_elf_cleanup_pgdir;    }    if (elf->e_magic != ELF_MAGIC) {        ret = -E_INVAL_ELF;        goto bad_elf_cleanup_pgdir;    }    struct proghdr __ph, *ph = &__ph;    uint32_t vm_flags, perm, phnum;    for (phnum = 0; phnum < elf->e_phnum; phnum ++) {        off_t phoff = elf->e_phoff + sizeof(struct proghdr) * phnum; //(3.2)循环读取程序的每个段的头部         if ((ret = load_icode_read(fd, ph, sizeof(struct proghdr), phoff)) != 0) {            goto bad_cleanup_mmap;        }        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)设置好虚拟地址与物理地址之间的映射        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;        }        off_t offset = ph->p_offset;        size_t off, size;        uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE);        ret = -E_NO_MEM;//(3.4)复制数据段和代码段         end = ph->p_va + ph->p_filesz;        while (start < end) {            if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {                ret = -E_NO_MEM;                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, offset)) != 0) {                goto bad_cleanup_mmap;            }            start += size, offset += size;        }//(3.5)建立BSS段        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) {                ret = -E_NO_MEM;                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;        }    }//关闭文件,加载程序结束    sysfile_close(fd);//(4)建立相应的虚拟内存映射表    vm_flags = VM_READ | VM_WRITE | VM_STACK;    if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) {        goto bad_cleanup_mmap;    }    assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-PGSIZE , PTE_USER) != NULL);    assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-2*PGSIZE , PTE_USER) != NULL);    assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-3*PGSIZE , PTE_USER) != NULL);    assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-4*PGSIZE , PTE_USER) != NULL);//(5)设置好用户栈    mm_count_inc(mm);    current->mm = mm;    current->cr3 = PADDR(mm->pgdir);    lcr3(PADDR(mm->pgdir));//(6)处理用户栈中传入的参数    uint32_t argv_size=0, i;    for (i = 0; i < argc; i ++) {        argv_size += strnlen(kargv[i],EXEC_MAX_ARG_LEN + 1)+1;    }    uintptr_t stacktop = USTACKTOP - (argv_size/sizeof(long)+1)*sizeof(long);    char** uargv=(char **)(stacktop  - argc * sizeof(char *));        argv_size = 0;    for (i = 0; i < argc; i ++) {        uargv[i] = strcpy((char *)(stacktop + argv_size ), kargv[i]);        argv_size +=  strnlen(kargv[i],EXEC_MAX_ARG_LEN + 1)+1;    }        stacktop = (uintptr_t)uargv - sizeof(int);    *(int *)stacktop = argc;//(7)设置进程的中断帧    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;    tf->tf_eip = elf->e_entry;    tf->tf_eflags = FL_IF;    ret = 0;//(8)错误处理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;}


实验结果:


ok,到目前为止所有ucore实验已经完成,由于时间有限,challenge实验部分未完成。



1 0
原创粉丝点击