• mit 6.828学习笔记不知道几--lab5


    exercise1:

    在文件inc/mmu.h中定义了

    #define FL_IOPL_MASK    0x00003000    // I/O Privilege Level bitmask

    x86处理器使用EFLAGS寄存器中的IOPL位来确定是否允许保护模式代码执行特殊的设备I/O指令,比如IN和OUT指令。

    我们只需要给文件系统环境提供“I/O privilege”即可。

    if(type== ENV_TYPE_FS)
            e->env_tf.tf_eflags |= FL_IOPL_MASK;

    make grade时通过“fs i/o” test。

    exercise 2 :

    在文件fs/ide.c 中,有磁盘读写函数

    int
    ide_read(uint32_t secno, void *dst, size_t nsecs)
    {
        ...
        return 0;
    }
    
    int
    ide_write(uint32_t secno, const void *src, size_t nsecs)
    {
            ...
        return 0;
    }

    bc_pgfault 这个函数处理页面错误,首先分配一个页面,然后从磁盘读取数据到页面中。

    flush_block 这个函数把包含va的块的内容flush到磁盘中去

    static void
    bc_pgfault(struct UTrapframe *utf)
    {  ...
        // LAB 5: you code here:
    //addr首先要页面对齐 addr = ROUNDDOWN(addr, PGSIZE); //分配一个页面 if ((r=sys_page_alloc(0, addr, PTE_U | PTE_P | PTE_W)) != 0) panic("bc_pgfault:sys_page_alloc filed:%e ,r"); //读取磁盘的内容 if ((r = ide_read(blockno * BLKSECTS, addr, BLKSECTS))!=0) panic("bc_pgfault:ide_read filed: %e ", r);   ... }
    void
    flush_block(void *addr)
    {
        uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE;
    
        if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
            panic("flush_block of bad va %08x", addr);
    
        // LAB 5: Your code here.
        int r;
        // LAB 5: Your code here.
        addr = ROUNDDOWN(addr, PGSIZE);
        if (va_is_mapped(addr) && va_is_dirty(addr)){
            if ((r = ide_write(blockno * BLKSECTS, addr, BLKSECTS)) < 0)
                panic("flush_block:ide_write filed: %e
    ", r);
            if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0)
                panic("flush_block:sys_page_map filed: %e
    ", r);
        
        }
        //panic("flush_block not implemented");
    }

    使用 make grade 结果如下:

     

     提到的函数

    // Check to see if the block bitmap indicates that block 'blockno' is free.
    // Return 1 if the block is free, 0 if not.
    bool
    block_is_free(uint32_t blockno)
    {
        if (super == 0 || blockno >= super->s_nblocks)
            return 0;
        if (bitmap[blockno / 32] & (1 << (blockno % 32)))
            return 1;
        return 0;
    }

    exercise 3:

    首先看看 free_block:

    // Mark a block free in the bitmap
    void
    free_block(uint32_t blockno)
    {
        // Blockno zero is the null pointer of block numbers.
        if (blockno == 0)
            panic("attempt to free zero block");
        bitmap[blockno/32] |= 1<<(blockno%32);
    
    }

    我们要借鉴的是将bitmap某一位设置为1的代码

    关于这个bitmap要讲两句:

    首先bitmap作为一个指针,指向了一块连续的区域,这个区域中的每一位都对应着一个块。

     也可以将bitmap视为一个数组,数组中的每一个偏移量都是一个unit32_t的数据,是32位的。

    简单画一个图:

     所以对于一个blockno,首先要得出他在bitmap[?],这个通过bitmap[blockno / 32]得到

    然后就是针对这一位进行检查,使用1 << (blockno % 32) 即可

    例如:

     至于为什么不是块1、块2...这样排列,这与1左移的方法有关。

    如果按照jos的代码来说,就应该是如上排列。

    如果采用1<<(31-blocno%32),就可以是块1、块2...这样的排列。

    只要标记为占用或者空闲时使用的左移的方法一样,就都可。

    int
    alloc_block(void)
    {
        // The bitmap consists of one or more blocks.  A single bitmap block
        // contains the in-use bits for BLKBITSIZE blocks.  There are
        // super->s_nblocks blocks in the disk altogether.
    
        // LAB 5: Your code here.
        uint32_t blockno;
    
        for (blockno = 0; blockno < super->s_nblocks; blockno++) {
            if (block_is_free(blockno)) {
                bitmap[blockno / 32] ^= 1 << (blockno % 32);
                flush_block(bitmap);
                return blockno;
            }
        }
        return -E_NO_DISK;
        //panic("alloc_block not implemented");
        return -E_NO_DISK;
    }

    make grade 应该通过 alloc——block

    exercise 4:

    可能会用到的几个函数:

    file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc)//寻找一个文件结构f中的第fileno个块指向的磁盘块编号放入ppdiskbno。如果filebno小于NDIRECT,则返回属于f.direct[NDIRECT]中的相应链接,否则返回f_indirect中查找的块。如果alloc为真且相应磁盘块不存在,则分配1个。
    dir_lookup(struct File *dir, const char *name, struct File **file)//在目录dir中查找名字为name的文件,如果找到则让file指向该文件结构体。
    dir_alloc_file(struct File *dir, struct File **file)//在dir对应的File结构体中分配1个File的指针连接给file,用于添加文件的操作。
    skip_slash(const char *p)//用于路径中的字符串处理,跳过斜杠。
    walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem)//path为从绝对路径的文件名,如果成功找到该文件,则把相应的文件结构体赋值给pf,其所在目录的文件结构体赋值给pdir,lastlem为失效时最后剩下的文件名。
    file_free_block(struct File *f, uint32_t filebno)//释放1个文件中的第filebno个磁盘块。此函数在file_truncate_blocks中被调用。
    file_truncate_blocks(struct File *f, off_t newsize)//将文件设置为缩小后的新大小,清空那些被释放的物理块。

    具体实现:

    static int
    file_block_walk(struct File* f, uint32_t filebno, uint32_t** ppdiskbno, bool alloc)
    {
        // LAB 5: Your code here.
        int r;
        //-E_INVAL if filebno is out of range (it's >= NDIRECT + NINDIRECT).
        if (filebno >= NDIRECT + NINDIRECT)
            return -E_INVAL;
        //如果他是直接块
        //fileno相当于偏移量
        if (filebno < NDIRECT) {
            if (ppdiskbno)
                *ppdiskbno = f->f_direct + filebno;
            return 0;
        }
        //-E_NOT_FOUND if the function needed to allocate an indirect block, but
        //alloc was 0.
        if (!alloc && !f->f_indirect)
            return -E_NOT_FOUND;
        //如果要分配,但是没有空闲的间接块了
        if (!f->f_indirect) {
            //尝试分配一个块,分配失败,
            if ((r = alloc_block()) < 0)
                return -E_NO_DISK;
            //分配成功,加入链表。记得清零
            f->f_indirect = r;
            memset(diskaddr(r), 0, BLKSIZE);
            flush_block(diskaddr(r));
        }
        //要分配并且有间接块
        if (ppdiskbno)
            *ppdiskbno = (uint32_t*)diskaddr(f->f_indirect) + filebno - NDIRECT;
        return 0;
        //panic("file_block_walk not implemented");
    }
    int
    file_get_block(struct File* f, uint32_t filebno, char** blk)
    {
        // LAB 5: Your code here.
        //-E_INVAL if filebno is out of range
        int r;
        uint32_t* ppdiskbno;
        if (filebno >= NDIRECT + NINDIRECT)
            return  -E_INVAL;
        //先调用file_walk_block函数找到文件中的目标块
        if ((r = file_block_walk(f, filebno, &ppdiskbno, 1)) < 0)
            return r;
        if (*ppdiskbno == 0) {
            if ((r = alloc_block()) < 0)
                return -E_NO_DISK;
            *ppdiskbno = r;
            memset(diskaddr(r), 0, BLKSIZE);
            flush_block(diskaddr(r));
    
        }
        *blk = diskaddr(*ppdiskbno);
        return 0;
        //panic("file_get_block not implemented");
    }

    make grade,应该通过“file_open”、“file_get_block”、“file_flush/file_truncated/file rewrite”和“testfile”。

     exercise 5 :

    Openfile结构体的定义

    //fs/server.c
    struct
    OpenFile { uint32_t o_fileid; // file id struct File* o_file; // mapped descriptor for open file int o_mode; // open mode struct Fd* o_fd; // Fd page };

    union Fsipc 定义:

    //inc/fs.h
    union Fsipc {
    struct Fsreq_open { char req_path[MAXPATHLEN]; int req_omode; } open; struct Fsreq_set_size { int req_fileid; off_t req_size; } set_size; struct Fsreq_read { int req_fileid; size_t req_n; } read; struct Fsret_read { char ret_buf[PGSIZE]; } readRet; struct Fsreq_write { int req_fileid; size_t req_n; char req_buf[PGSIZE - (sizeof(int) + sizeof(size_t))]; } write; struct Fsreq_stat { int req_fileid; } stat; struct Fsret_stat { char ret_name[MAXNAMELEN]; off_t ret_size; int ret_isdir; } statRet; struct Fsreq_flush { int req_fileid; } flush; struct Fsreq_remove { char req_path[MAXPATHLEN]; } remove; // Ensure Fsipc is one page char _pad[PGSIZE]; };
    int
    serve_read(envid_t envid, union Fsipc *ipc)
    {
        struct Fsreq_read *req = &ipc->read;
        struct Fsret_read *ret = &ipc->readRet;
    
        if (debug)
            cprintf("serve_read %08x %08x %08x
    ", envid, req->req_fileid, req->req_n);
    
        // Lab 5: Your code here:
    
        struct OpenFile* o;
        int r, req_n;
        // Look up an open file for envid.
        if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
            return r;
        //因为ipc最多只能传一个页面 所以取max{PGSIZE,req_req_n}
        req_n = req->req_n > PGSIZE ? PGSIZE : req->req_n;
        if ((r = file_read(o->o_file, ret->ret_buf, req_n, o->o_fd->fd_offset)) < 0)
            return r;
        //读入了数据,偏移量要增加
        o->o_fd->fd_offset += r;
    
        return r;
        
    }

    其实真正读的工作是由file_read完成的,server_read只是提供了一个rpc接口

    make grade应该通过“serve_open/file_stat/file_close”和“file_read”获得70/150的分数。

     exercise 6:

    这个函数的实现十分类似于serve_write()。

    int
    serve_write(envid_t envid, struct Fsreq_write* req)
    {
        if (debug)
            cprintf("serve_write %08x %08x %08x
    ", envid, req->req_fileid, req->req_n);
    
        // LAB 5: Your code here.
        struct OpenFile* o;
        int r, req_n;
        // Look up an open file for envid.
        if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
            return r;
        req_n = req->req_n > PGSIZE ? PGSIZE : req->req_n;
        if ((r = file_write(o->o_file, req->req_buf, req_n, o->o_fd->fd_offset)) < 0)
            return r;
        o->o_fd->fd_offset += r;
    
        return r;
        //panic("serve_write not implemented");
    }

    这个函数的实现类似于上面的 devfile_read()。

    但是此处有一个问题:

    static ssize_t
    devfile_read(struct Fd* fd, void* buf, size_t n)
    {
        ...
        if ((r = fsipc(FSREQ_READ, NULL)) < 0)
            return r;
        ...
        memmove(buf, fsipcbuf.readRet.ret_buf, r);
        ...
    }

    这两行代码在devfile_read()中需要交换位置。为什么

    static ssize_t
    devfile_write(struct Fd *fd, const void *buf, size_t n)
    {
        // Make an FSREQ_WRITE request to the file system server.  Be
        // careful: fsipcbuf.write.req_buf is only so large, but
        // remember that write is always allowed to write *fewer*
        // bytes than requested.
        // LAB 5: Your code here
    
        int r;
        //它只能这么大
        if (n > sizeof(fsipcbuf.write.req_buf))
            n = sizeof(fsipcbuf.write.req_buf);
        fsipcbuf.write.req_fileid = fd->fd_file.id;
        fsipcbuf.write.req_n = n;
        memmove(fsipcbuf.write.req_buf, buf, n);
        if ((r = fsipc(FSREQ_WRITE, NULL)) < 0)
            return r;
        assert(r <= n);
        assert(r <= PGSIZE);
        //memmove(fsipcbuf.write.req_buf, buf, n);
        return r;
    
        //panic("devfile_write not implemented");
        
    }

     exercise 7:

    static int
    sys_env_set_trapframe(envid_t envid, struct Trapframe *tf)
    {
        // LAB 5: Your code here.
        // Remember to check whether the user has supplied us with a good
        // address!
        struct Env *e;
            int r;
    
            if ((r = envid2env(envid, &e, true)) < 0)
                    return -E_BAD_ENV;
            user_mem_assert(e, tf, sizeof(struct Trapframe), PTE_U);

          tf->tf_eflags |= FL_IF;
          tf->tf_eflags &= ~FL_IOPL_MASK; //普通进程不能有IO权限
          tf->tf_cs |= 3;
          e->env_tf = *tf;

    return 0;
        //panic("sys_env_set_trapframe not implemented");
    }

    记得修改syscall.c的分支

    case (SYS_env_set_trapframe):
                return sys_env_set_trapframe(a1,(struct Trapframe *)a2);

    以及init.c中

    #if defined(TEST)
        // Don't touch -- used by grading script!
        ENV_CREATE(TEST, ENV_TYPE_USER);
    #else
        // Touch all you want.
    
        //ENV_CREATE(user_icode, ENV_TYPE_USER);
    
        //ENV_CREATE(user_primes, ENV_TYPE_USER);
        ENV_CREATE(user_spawnhello, ENV_TYPE_USER);
        /*ENV_CREATE(user_yield, ENV_TYPE_USER);
        ENV_CREATE(user_yield, ENV_TYPE_USER);
        ENV_CREATE(user_yield, ENV_TYPE_USER);*/
        
        //ENV_CREATE(user_dumbfork, ENV_TYPE_USER);
    
    #endif // TEST*

    make grade 通过  spawn via spawnhello

     exercise 8:

    记得修改init.c恢复原状

    static int
    duppage(envid_t envid, unsigned pn)
    {
        //这个函数用来复制映射关系
        //对于UTOP下面地址空间中的每个可写页面或写时复制页面,
        //父类(1)要调用duppage, 写时复制的页面映射到子进程的地址空间,
        //    (2) 在自己的地址空间中重新映射写时复制的页面。
    
        int r;
        void* addr = (void*)(pn << 12);//address  pn*PGSIZE
        envid_t fu_id = sys_getenvid();
        //Lab 5 code here:
        //如果页表条目设置了PTE_SHARE位,那么直接复制映射即可。
        if (uvpt[pn] & PTE_SHARE) {
            if ((r = sys_page_map(sys_getenvid(), addr, envid, addr, uvpt[pn] & PTE_SYSCALL)) < 0) {
                panic("duppage: page mapping failed: %e", r);
                return r;
            }
        }
        else {
            if (uvpt[pn] & (PTE_W | PTE_COW)) {
                //  If the page is writable or copy-on-write,
                // the new mapping must be created copy-on-write,
                //父进程的地址空间映射给了子进程
                r = sys_page_map(fu_id, (void*)addr, envid, (void*)addr, PTE_COW | PTE_U);
                if (r != 0)
                    return r;
                //父进程这里要重新映射一遍,区别是之前有可能是writable,现在只能是COW,so?
                //难道是因为现在父子进程都映射着同一个物理页,如果父进程还是可写的话,就会影响子进程
                r = sys_page_map(fu_id, (void*)addr, fu_id, (void*)addr, PTE_COW | PTE_U);
                if (r != 0)
                    return r;
            }
            else {
                r = sys_page_map(fu_id, (void*)addr, envid, (void*)addr, uvpt[pn] & PTE_SYSCALL);
                if (r != 0)
                    return r;
            }
        }
        return 0;
        
    
    }
    static int
    copy_shared_pages(envid_t child)
    {
        int i,r;
        // LAB 5: Your code here.
        //类似与fork.c中的fork(),将父进程的地址空间复制给子进程
        //它应该循环遍历当前进程中的所有页表条目(就像fork所做的那样)
        //将设置了PTE_SHARE位的任何页映射到子进程
        //这里不用duppage,因为我们是直接共享了地址空间,所以添加映射即可
        //而dupage只是复制了到了子进程里面,但是全局没有注册
        
        for (i = 0; i < PGNUM(USTACKTOP); i++) {
            
            if ((uvpd[i / 1024] & PTE_P) && (uvpt[i] & PTE_P) && (uvpt[i] & PTE_SHARE)) { 
                //相比于fork(),这里多了一个判断条件, uvpt[i] & PTE_SHARE)
                if ((r = sys_page_map(0, PGADDR(i / 1024, i % 1024, 0), child, PGADDR(i / 1024, i % 1024, 0), uvpt[i] & PTE_SYSCALL)) < 0)
                    return r;
            }
        }
        return 0;
    }

    make run-testpteshare:应该看到“fork handle PTE_SHARE right、spawn handle PTE_SHARE right

     

     

    make run-testfdsharing:应该看到read in child succeeded、read in parent succeeded

    exercise 9:

    这个联系很简单,在trap_dispatch中添加分支即可:

       // Handle keyboard and serial interrupts.
        // LAB 5: Your code here.
        else if (tf->tf_trapno == IRQ_OFFSET + IRQ_KBD) {
            kbd_intr();
            return;
        }
        else if (tf->tf_trapno == IRQ_OFFSET + IRQ_SERIAL) {
            serial_intr();
            return;
        }

    运行make run-testkbd测试代码,并输入一些字符,按下回车,他应该显示你刚刚输入的字符。

     exercise 10:

    // LAB 5: Your code here.
                if ((fd = open(t, O_RDONLY)) < 0) {
                    cprintf("open %s for read: %e", t, fd);
                    exit();
                }
                if (fd != 0) {
                    dup(fd, 0); 
                    close(fd);
                }

    make grade

     

  • 相关阅读:
    <转>Logistic回归总结
    特征选择和降维的区别
    <转>SVM实现之SMO算法
    <转>KMP算法详解
    <转>主成分分析(Principal components analysis)-最大方差解释,最小平方差解释
    <转>与EM相关的两个算法-K-mean算法以及混合高斯模型
    <转>E-M算法
    隐马尔科夫
    机器学习之判别式模型和生成式模型
    复制图片
  • 原文地址:https://www.cnblogs.com/luo-he/p/14013192.html
Copyright © 2020-2023  润新知