lab10
mmap(hard)
系统调用的添加过程这里就不赘述了,首先,我们先添加vma结构体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| struct VMA{ uint64 addr; uint64 size; uint64 prot; int fd; int used ; int offset; struct file* file; int flag; };
struct proc { struct spinlock lock; ... struct VMA vma[16]; };
memset(&p->vma, 0, sizeof(p->vma));
|
同时,我们对mmap系统调用进行处理
- 写回问题:当设置MAP_SHARED时要检查权限。
- sbrk问题:我们只能在这里增加sbrk,因为mmap要求内存是连续的。
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
| uint64 sys_mmap(void) { uint64 addr, err = 0xffffffffffffffff; int len, prot, flags, fd, offset; struct file *f; if(argaddr(0, &addr) || argint(1, &len) || argint(2, &prot) || argint(3, &flags) || argfd(4, &fd, &f) || argint(5, &offset)) return err;
struct proc *p = myproc(); if(p->sz + len > MAXVA) return err; if(f->writable == 0 && (prot & PROT_WRITE) != 0 && flags == MAP_SHARED) return err;
addr = p->sz;
for(int i = 0; i < MAX_MMAP_REGIONS; i++) { if(p->vma[i].used == 0) { p->vma[i].addr = addr; p->vma[i].fd = fd; p->vma[i].prot = prot; p->vma[i].used = 1; p->vma[i].size = len; p->vma[i].offset = offset; p->vma[i].file = f; p->vma[i].flag = flags; filedup(f); break; } } p->sz += len; return addr; }
|
接下来设置中断处理函数,吸取cow的教训,我们将函数实现放入vm.c实现模块化。
1 2 3 4 5 6 7 8 9 10 11 12
| else if(r_scause() == 13 || r_scause() == 15) { uint64 va = r_stval(); if(va > p->sz || va < p->trapframe->sp) p->killed = 1; else if( mapfile(va)){ p->killed = 1; }
} else {
|
- 首先,索引对应页面,判断参数合法性
- 设置权限,取该虚拟地址的页表首地址,读入文件分配映射页面。
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
| int mapfile(uint64 va) { int i; int flag = PTE_U; int prot; char *mem; int offset = 0; struct proc* p = myproc(); for(i = 0; i < MAX_MMAP_REGIONS; i ++) { if(p->vma[i].used == 0) continue; if(p->vma[i].addr <= va && p->vma[i].addr + p->vma[i].size - 1 >= va ) break; } if(i == MAX_MMAP_REGIONS) { return -1; }
prot = p->vma[i].prot; if(prot & PROT_READ) flag |= PTE_R; if(prot & PROT_WRITE) flag |= PTE_W; if(prot & PROT_EXEC) flag |= PTE_X;
if((mem = kalloc()) == 0) return -1; memset(mem, 0, PGSIZE); offset = PGROUNDDOWN(p->vma[i].offset + va - p->vma[i].addr) ; ilock(p->vma[i].file->ip); if(readi(p->vma[i].file->ip, 0,(uint64)mem, offset, PGSIZE) == 0) { iunlock(p->vma[i].file->ip); kfree(mem); return -1; } iunlock(p->vma[i].file->ip);
if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)mem, flag) < 0) { kfree(mem); return -1; } return 0;
}
|
接下来处理munmap,因为释放只会发生在映射区域首部和末尾,因此可以用size来判断文件是否已被全部解除映射。
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
| uint64 sys_munmap(void) { uint64 err = 0xffffffffffffffff; uint64 addr; int len; int i; struct proc *p = myproc(); if(argaddr(0, &addr) || argint(1, &len)) return err;
for(i = 0; i < MAX_MMAP_REGIONS; i ++) { if(p->vma[i].addr == 0) continue; if(p->vma[i].size >= len) { if(addr == p->vma[i].addr) { p->vma[i].size -= len; p->vma[i].addr += len; break; } if(addr == p->vma[i].addr + len) { p->vma[i].size -= len; break; } } } if(i == MAX_MMAP_REGIONS) return err;
if(p->vma[i].flag == MAP_SHARED &&(p->vma[i].prot & PROT_WRITE) != 0) filewrite(p->vma[i].file, addr, len); uvmunmap(p->pagetable, addr, len/PGSIZE, 1);
if(p->vma[i].size == 0) { fileclose(p->vma[i].file); p->vma[i].used = 0; }
return 0; }
|
接下来是处理子进程问题,我们需要复制VMA结构体,并添加对文件的引用计数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int fork(void) { ...
for(i = 0; i < MAX_MMAP_REGIONS; ++i) { if(p->vma[i].used) { memmove(&np->vma[i], &p->vma[i], sizeof(p->vma[i])); filedup(p->vma[i].file); } }
release(&np->lock);
return pid; }
|
在退出时判断是否需要写入文件。由于我们的内存全部分配在堆上,所以可以由proc_freepagetable
取消映射和释放内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void exit(int status) { struct proc *p = myproc();
if(p == initproc) panic("init exiting"); ... for(int i = 0; i < MAX_MMAP_REGIONS; ++i) { if(p->vma[i].used) { if(p->vma[i].flag == MAP_SHARED && (p->vma[i].prot & PROT_WRITE) != 0) { filewrite(p->vma[i].file, p->vma[i].addr, p->vma[i].size); } fileclose(p->vma[i].file); p->vma[i].used = 0; } }
|