$ mmaptest mmap_test starting test mmap f test mmap f: OK test mmap private test mmap private: OK test mmap read-only test mmap read-only: OK test mmap read/write test mmap read/write: OK test mmap dirty test mmap dirty: OK test not-mapped unmap test not-mapped unmap: OK test mmap two files test mmap two files: OK mmap_test: ALL OK fork_test starting fork_test OK mmaptest: all tests succeeded
#define NVMA 16 // maximum number of vma in a process // VMA state structvma{ int used; uint64 addr; // start va addr of the current vma uint len; // vma length uint prot; // memory protection of the mapping uint flags; // whether updates are carried through to the underlying file structfile *f;// target file uint offset; // map starts at offset in the file }
// Per-process state structproc { structspinlocklock;
// p->lock must be held when using these: enumprocstatestate;// Process state void *chan; // If non-zero, sleeping on chan int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parent's wait int pid; // Process ID
// wait_lock must be held when using this: structproc *parent;// Parent process
// these are private to the process, so p->lock need not be held. uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) pagetable_t pagetable; // User page table structtrapframe *trapframe;// data page for trampoline.S structcontextcontext;// swtch() here to run process structfile *ofile[NOFILE];// Open files structinode *cwd;// Current directory structvmavma[NVMA];// Table of mapped regions char name[16]; // Process name (debugging) };
if(addr != 0) panic("sys_mmap: addr not set 0"); if(offset != 0) panic("sys_mmap: offset not set 0");
// Check if file's prot bits match flags and file perms. // You cannot write back any changes to a unwritable file. structfile *f = p->ofile[fd]; if((!(f->writable)) && (flags & MAP_SHARED) && (prot & PROT_WRITE)){ printf("sys_mmap: cannot write back any changes to a unwritable file.\n"); return-1; } // you cannot read from a unreadable file either if((!(f->readable)) && (prot & PROT_READ)){ printf("sys_mmap: cannot read from a unreadable file.\n"); return-1; }
// Find free VMA, and calculate where to put mmap-ed user momory // mmap-ed memory grows top-down from trampoline page structvma *vma =0; uint64 min_mmap_addr = TRAPFRAME; for (int i = 0; i < NVMA; i++){ structvma *v = &p->vma[i]; if(!v->used){ if(!vma){ vma = v; v->used = 1; } } elseif(v->addr < min_mmap_addr){ min_mmap_addr = PGROUNDDOWN(v->addr); } } if(!vma){ printf("sys_map: unable to find free VMA\n"); return-1; // Unable to find free VMA }
// Fill in state info into vma vma->len = length; vma->prot = prot; vma->flags = flags; vma->f = filedup(f); // Increase the file's reference count vma->offset = offset; vma->addr = min_mmap_addr - PGROUNDUP(length);
// Handle page fault caused by mmaped va, // Return 0 on success, -1 on failure, -2 on invalid va intmmap_handler(uint64 va, int scause) { structproc *p = myproc(); int vma_index = -1; if (va >= MAXVA) { printf("va cannot be greater than MAXVA: %p\n", va); return-2; } // Scan the VMA list to match va for (int i = 0; i < NVMA; i++){ structvma* vma = &p->vma[i]; if(vma->used == 1 && va >= vma->addr && va < vma->addr + PGROUNDUP(vma->len)){ vma_index = i; break; } } if(vma_index == -1) { return-2; // VMA not found } structvma *v = &p->vma[vma_index];
// Map the page to user space int perm = PTE_U; if(v->prot & PROT_READ){ perm |= PTE_R; } if(v->prot & PROT_WRITE){ perm |= PTE_W; } if(v->prot & PROT_EXEC){ perm |= PTE_X; } if(mappages(p->pagetable, va, PGSIZE, (uint64)pa, perm) < 0){ printf("mmap_handler: failed to map page to user space. va: %p, pa: %p\n", va, pa); kfree(pa); return-1; // Failed to map page to user space } return0; }
// Remove npages of mappings starting from va. va must be // page-aligned. The mappings must exist. // Optionally free the physical memory. void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) { uint64 a; pte_t *pte;
if((va % PGSIZE) != 0) panic("uvmunmap: not aligned");
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){ if((pte = walk(pagetable, a, 0)) == 0) panic("uvmunmap: walk"); if((*pte & PTE_V) == 0) // panic("uvmunmap: not mapped"); continue; if(PTE_FLAGS(*pte) == PTE_V) panic("uvmunmap: not a leaf"); if(do_free){ uint64 pa = PTE2PA(*pte); kfree((void*)pa); } *pte = 0; } }
// Exit the current process. Does not return. // An exited process remains in the zombie state // until its parent calls wait(). void exit(int status) { structproc *p = myproc();
// Create a new process, copying the parent. // Sets up child kernel stack to return as if from fork() system call. int fork(void) { int i, pid; structproc *np; structproc *p = myproc();