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

 

原文地址:https://www.cnblogs.com/luo-he/p/14013192.html