MIT6.828 Lab5

最崩溃的一集…Lab倒是很简单,只是文件系统一启用,之前代码的好多问题都显现出来了,然后就是debugggg.

Lab 5: File system, Spawn and Shell

Getting Started

切换分支之后编译有点问题,改一改:


在GNUmakefile中加上-Wno-address-of-packed-member

将fs.h中定义的全局变量改为外部变量,并在fs.c中定义.

1
2
extern struct Super *super;		// superblock
extern uint32_t *bitmap; // bitmap blocks mapped in memory

改下输出,lab4满分通过.

File system preliminaries

文件系统的设计就不在这里说了,详见《HQOS 设计与实现》.

The File System

The Block Cache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// Fault any disk block that is read in to memory by
// loading it from disk.
static void
bc_pgfault(struct UTrapframe *utf)
{
void *addr = (void *) utf->utf_fault_va;
uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE;
int r;

// Check that the fault was within the block cache region
if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
panic("page fault in FS: eip %08x, va %08x, err %04x",
utf->utf_eip, addr, utf->utf_err);

// Sanity check the block number.
if (super && blockno >= super->s_nblocks)
panic("reading non-existent block %08x\n", blockno);

// Allocate a page in the disk map region, read the contents
// of the block from the disk into that page.
// Hint: first round addr to page boundary. fs/ide.c has code to read
// the disk.
//
// LAB 5: you code here:
addr = ROUNDDOWN(addr,PGSIZE);
if((r = sys_page_alloc(0,addr,PTE_SYSCALL)))
{
panic("in bc_pgfault, sys_page_alloc: %e",r);
}

if((r = ide_read(blockno*BLKSECTS,addr,BLKSECTS)))
{
panic("in bc_pgfault, ide_read");
}
// Clear the dirty bit for the disk block page since we just read the
// block from disk
if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0)
panic("in bc_pgfault, sys_page_map: %e", r);

// Check that the block we read was allocated. (exercise for
// the reader: why do we do this *after* reading the block
// in?)
if (bitmap && block_is_free(blockno))
panic("reading free block %08x\n", blockno);
}

// Flush the contents of the block containing VA out to disk if
// necessary, then clear the PTE_D bit using sys_page_map.
// If the block is not in the block cache or is not dirty, does
// nothing.
// Hint: Use va_is_mapped, va_is_dirty, and ide_write.
// Hint: Use the PTE_SYSCALL constant when calling sys_page_map.
// Hint: Don't forget to round addr down.
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;
addr = ROUNDDOWN(addr,PGSIZE);
if(va_is_mapped(addr)&&va_is_dirty(addr))
{
if(ide_write(blockno*BLKSECTS,addr,BLKSECTS))
{
panic("in flush_block, ide_write");
}
if((r = sys_page_map(0,addr,0,addr,uvpt[PGNUM(addr)] & PTE_SYSCALL)))
{
panic("in flush_block, sys_page_map: %e",r);
}
}
}

The Block Bitmap

更改bitmap之后记得刷新bitmap对应块缓存.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.
for(int i = 0;i<super->s_nblocks;++i)
{
if(block_is_free(i))
{
bitmap[i/32] &= ~(1<<(i%32));
flush_block((void*)bitmap+BLKSIZE*(i/BLKBITSIZE));
return i;
}
}
return -E_NO_DISK;
}

File Operations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Find the disk block number slot for the 'filebno'th block in file 'f'.
// Set '*ppdiskbno' to point to that slot.
// The slot will be one of the f->f_direct[] entries,
// or an entry in the indirect block.
// When 'alloc' is set, this function will allocate an indirect block
// if necessary.
//
// Returns:
// 0 on success (but note that *ppdiskbno might equal 0).
// -E_NOT_FOUND if the function needed to allocate an indirect block, but
// alloc was 0.
// -E_NO_DISK if there's no space on the disk for an indirect block.
// -E_INVAL if filebno is out of range (it's >= NDIRECT + NINDIRECT).
//
// Analogy: This is like pgdir_walk for files.
// Hint: Don't forget to clear any block you allocate.
static int
file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc)
{
// LAB 5: Your code here.
int r;
if(filebno >= NDIRECT+NINDIRECT)
return -E_INVAL;
if(filebno<NDIRECT)
{
*ppdiskbno = &(f->f_direct[filebno]);
return 0;
}
else
{
if(f->f_indirect==0)
{
if(alloc)
{
if((r = alloc_block())<0)
{
return r;
}
memset(diskaddr(r), 0, BLKSIZE);
f->f_indirect = r;
}
else
return -E_NOT_FOUND;
}
*ppdiskbno = &(((uint32_t*)(diskaddr(f->f_indirect)))[filebno-NDIRECT]);
return 0;
}
}

// Set *blk to the address in memory where the filebno'th
// block of file 'f' would be mapped.
//
// Returns 0 on success, < 0 on error. Errors are:
// -E_NO_DISK if a block needed to be allocated but the disk is full.
// -E_INVAL if filebno is out of range.
//
// Hint: Use file_block_walk and alloc_block.
int
file_get_block(struct File *f, uint32_t filebno, char **blk)
{
// LAB 5: Your code here.
uint32_t* ppdisk;
int r;
if((r = file_block_walk(f,filebno,&ppdisk,1)))
{
return r;
}
if(*ppdisk==0)
{
if((r = alloc_block())<0)
{
return r;
}
*ppdisk = r;
memset(diskaddr(r), 0, BLKSIZE);
}
*blk = diskaddr(*ppdisk);
return 0;
}

The file system interface

用户文件读写流程分析
open

用户进程使用lib中的open函数,指定文件路径和以何种模式打开.open函数为进程分配一个未使用的文件描述符号fd,对应着0xD0000000处MAXFD个struct Fd结构中的一个.然后通过IPC进程间通信向fs server进程发送FSREQ_OPEN的请求.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int
open(const char *path, int mode)
{
int r;
struct Fd *fd;
if (strlen(path) >= MAXPATHLEN)
return -E_BAD_PATH;

if ((r = fd_alloc(&fd)) < 0)
return r;

strcpy(fsipcbuf.open.req_path, path);

fsipcbuf.open.req_omode = mode;

if ((r = fsipc(FSREQ_OPEN, fd)) < 0) {
fd_close(fd, 0);
return r;
}
return fd2num(fd);
}

server进程接收到open请求,根据模式尝试打开(创建)文件:先调用openfile_alloc函数在opentab中分配一个struct OpenFile结构o,包括为o->fd分配物理页.然后调用file_open,从根目录开始遍历直到找到用户请求文件的struct File结构(JOS实现中目录的内容即是目录中的文件的struct File结构),设置o->file指向该结构.通过IPC通信的交互,最终实现将为o->fd分配的物理页映射到用户进程中该fd结构的对应虚拟地址.

这里理解一下openfile_alloc函数中的case1.当该opentab条目的o_fd还未使用过,分配一个物理页,此时pageref为1,再映射到用户,pageref为2,下一次打开文件就应该跳过这个条目.若pageref为1时,意味着除了fs server本身,已经没有用户进程使用该文件,所以该条目依然为free状态,可以分配.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Allocate an open file.
int
openfile_alloc(struct OpenFile **o)
{
int i, r;

// Find an available open-file table entry
for (i = 0; i < MAXOPEN; i++) {
switch (pageref(opentab[i].o_fd)) {
case 0:
if ((r = sys_page_alloc(0, opentab[i].o_fd, PTE_P|PTE_U|PTE_W)) < 0)
return r;
/* fall through */
case 1:
opentab[i].o_fileid += MAXOPEN;
*o = &opentab[i];
memset(opentab[i].o_fd, 0, PGSIZE);
return (*o)->o_fileid;
}
}
return -E_MAX_OPEN;
}

既然未来的读写操作都是server完成的,把o->fd映射到用户进程有什么用呢?从代码上能看出来的用途大概是1)对某文件打开模式的检查 2)将对特定文件类型(devid)的IO操作dispatch到特定的函数实现,如devfile,pipe,console.

(这Fd映射还是以可写权限映射的,没道理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static struct Dev *devtab[] =
{
&devfile,
&devsock,
&devpipe,
&devcons,
0
};


struct Dev devfile =
{
.dev_id = 'f',
.dev_name = "file",
.dev_read = devfile_read,
.dev_close = devfile_flush,
.dev_stat = devfile_stat,
.dev_write = devfile_write,
.dev_trunc = devfile_trunc
};
read

lib中的read函数根据文件的类型(devid)分发到特定的IO函数实现,这里以devfile_read为例.

向server发送FSREQ_READ请求,server从硬盘中读取对应文件数据到内存(别忘了JOS实现中是将整个3GB的硬盘映射到fs server的虚拟地址空间中)中,再经两次拷贝到用户进程要求的缓冲区中.

可以看出devfile_read设计成每次最多读取PGSIZE字节,因为这受限于我们的IPC机制.记得在server_read中处理参数n防止缓冲区溢出.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static ssize_t
devfile_read(struct Fd *fd, void *buf, size_t n)
{
// Make an FSREQ_READ request to the file system server after
// filling fsipcbuf.read with the request arguments. The
// bytes read will be written back to fsipcbuf by the file
// system server.
int r;

fsipcbuf.read.req_fileid = fd->fd_file.id;
fsipcbuf.read.req_n = n;
if ((r = fsipc(FSREQ_READ, NULL)) < 0)
{
return r;

}
assert(r <= n);
assert(r <= PGSIZE);
memmove(buf, fsipcbuf.readRet.ret_buf, r);
return r;
}

实现中的union Fsipc挺有意思.

1
2
3
4
5
union Fsipc {
struct Fsret_read {
char ret_buf[PGSIZE];
} readRet;
}
write

和read区别不大.

lab代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
   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\n", envid, req->req_fileid, req->req_n);
// Lab 5: Your code here:
int r;
struct OpenFile *po;

if ((r = openfile_lookup(envid, req->req_fileid, &po)) < 0)
return r;

//防止溢出
if(req->req_n>PGSIZE)
req->req_n = PGSIZE;

if ((r = file_read(po->o_file, ret->ret_buf, req->req_n, po->o_fd->fd_offset)) < 0)
return r;
po->o_fd->fd_offset += r;
return r;
}

int
serve_write(envid_t envid, struct Fsreq_write *req)
{
if (debug)
cprintf("serve_write %08x %08x %08x\n", envid, req->req_fileid, req->req_n);
// LAB 5: Your code here.
int r;
struct OpenFile *po;
if ((r = openfile_lookup(envid, req->req_fileid, &po)) < 0)
return r;
if ((r = file_write(po->o_file, req->req_buf, req->req_n, po->o_fd->fd_offset)) < 0)
return r;
po->o_fd->fd_offset += r;
return r;
}

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
if (n > sizeof(fsipcbuf.write.req_buf))
panic("devfile_write: invalid n\n");
fsipcbuf.write.req_fileid = fd->fd_file.id;
fsipcbuf.write.req_n = n;
memmove(fsipcbuf.write.req_buf, buf, n);

return fsipc(FSREQ_WRITE, NULL);
}

Spawning Processes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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, 1)) < 0)
return r;
user_mem_assert(curenv, (void *)tf, sizeof(struct Trapframe), 0);

memmove(&e->env_tf, tf, sizeof(struct Trapframe));
e->env_tf.tf_cs |= 3;
e->env_tf.tf_eflags |= FL_IF;
e->env_tf.tf_eflags &= ~FL_IOPL_MASK;
return 0;
}

Sharing library state across fork and spawn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Copy the mappings for shared pages into the child address space.
static int
copy_shared_pages(envid_t child)
{
// LAB 5: Your code here.
extern volatile pde_t uvpd[];
extern volatile pte_t uvpt[];
int r;

for(uintptr_t va=0;va<UTOP;)
{
if((uvpd[va >> PDXSHIFT]&PTE_P)==0)
{
va += PGSIZE*NPTENTRIES;
continue;
}

int perm = uvpt[va >> PTXSHIFT] & PTE_SYSCALL;
if ((perm & PTE_P) == 0) { // Page not mapped.
va += PGSIZE;
continue;
}
if (perm & PTE_SHARE) {
if ((r = sys_page_map(0, (void *)va, child, (void *)va, perm)) < 0)
return r;
}
va += PGSIZE;
}
return 0;
}


  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 翰青HanQi

请我喝杯咖啡吧~

支付宝
微信