#include "kernel/param.h" #include "kernel/fcntl.h" #include "kernel/types.h" #include "kernel/stat.h" #include "kernel/riscv.h" #include "kernel/fs.h" #include "user/user.h" void mmap_test(); void fork_test(); char buf[BSIZE]; #define MAP_FAILED ((char *) -1) int main(int argc, char *argv[]) { mmap_test(); fork_test(); printf("mmaptest: all tests succeeded\n"); exit(0); } char *testname = "???"; void err(char *why) { printf("mmaptest: %s failed: %s, pid=%d\n", testname, why, getpid()); exit(1); } // // check the content of the two mapped pages. // void _v1(char *p) { int i; for (i = 0; i < PGSIZE*2; i++) { if (i < PGSIZE + (PGSIZE/2)) { if (p[i] != 'A') { printf("mismatch at %d, wanted 'A', got 0x%x\n", i, p[i]); err("v1 mismatch (1)"); } } else { if (p[i] != 0) { printf("mismatch at %d, wanted zero, got 0x%x\n", i, p[i]); err("v1 mismatch (2)"); } } } } // // create a file to be mapped, containing // 1.5 pages of 'A' and half a page of zeros. // void makefile(const char *f) { int i; int n = PGSIZE/BSIZE; unlink(f); int fd = open(f, O_WRONLY | O_CREATE); if (fd == -1) err("open"); memset(buf, 'A', BSIZE); // write 1.5 page for (i = 0; i < n + n/2; i++) { if (write(fd, buf, BSIZE) != BSIZE) err("write 0 makefile"); } if (close(fd) == -1) err("close"); } void mmap_test(void) { int fd; int i; const char * const f = "mmap.dur"; printf("mmap_test starting\n"); testname = "mmap_test"; // // create a file with known content, map it into memory, check that // the mapped memory has the same bytes as originally written to the // file. // makefile(f); if ((fd = open(f, O_RDONLY)) == -1) err("open"); printf("test mmap f\n"); // // this call to mmap() asks the kernel to map the content // of open file fd into the address space. the first // 0 argument indicates that the kernel should choose the // virtual address. the second argument indicates how many // bytes to map. the third argument indicates that the // mapped memory should be read-only. the fourth argument // indicates that, if the process modifies the mapped memory, // that the modifications should not be written back to // the file nor shared with other processes mapping the // same file (of course in this case updates are prohibited // due to PROT_READ). the fifth argument is the file descriptor // of the file to be mapped. the last argument is the starting // offset in the file. // char *p = mmap(0, PGSIZE*2, PROT_READ, MAP_PRIVATE, fd, 0); if (p == MAP_FAILED) err("mmap (1)"); _v1(p); if (munmap(p, PGSIZE*2) == -1) err("munmap (1)"); printf("test mmap f: OK\n"); printf("test mmap private\n"); // should be able to map file opened read-only with private writable // mapping p = mmap(0, PGSIZE*2, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (p == MAP_FAILED) err("mmap (2)"); if (close(fd) == -1) err("close"); _v1(p); for (i = 0; i < PGSIZE*2; i++) p[i] = 'Z'; if (munmap(p, PGSIZE*2) == -1) err("munmap (2)"); printf("test mmap private: OK\n"); printf("test mmap read-only\n"); // check that mmap doesn't allow read/write mapping of a // file opened read-only. if ((fd = open(f, O_RDONLY)) == -1) err("open"); p = mmap(0, PGSIZE*3, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p != MAP_FAILED) err("mmap call should have failed"); if (close(fd) == -1) err("close"); printf("test mmap read-only: OK\n"); printf("test mmap read/write\n"); // check that mmap does allow read/write mapping of a // file opened read/write. if ((fd = open(f, O_RDWR)) == -1) err("open"); p = mmap(0, PGSIZE*3, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) err("mmap (3)"); if (close(fd) == -1) err("close"); // check that the mapping still works after close(fd). _v1(p); // write the mapped memory. for (i = 0; i < PGSIZE*2; i++) p[i] = 'Z'; // unmap just the first two of three pages of mapped memory. if (munmap(p, PGSIZE*2) == -1) err("munmap (3)"); printf("test mmap read/write: OK\n"); printf("test mmap dirty\n"); // check that the writes to the mapped memory were // written to the file. if ((fd = open(f, O_RDWR)) == -1) err("open"); for (i = 0; i < PGSIZE + (PGSIZE/2); i++){ char b; if (read(fd, &b, 1) != 1) err("read (1)"); if (b != 'Z') err("file does not contain modifications"); } if (close(fd) == -1) err("close"); printf("test mmap dirty: OK\n"); printf("test not-mapped unmap\n"); // unmap the rest of the mapped memory. if (munmap(p+PGSIZE*2, PGSIZE) == -1) err("munmap (4)"); printf("test not-mapped unmap: OK\n"); printf("test mmap two files\n"); // // mmap two files at the same time. // int fd1; if((fd1 = open("mmap1", O_RDWR|O_CREATE)) < 0) err("open mmap1"); if(write(fd1, "12345", 5) != 5) err("write mmap1"); char *p1 = mmap(0, PGSIZE, PROT_READ, MAP_PRIVATE, fd1, 0); if(p1 == MAP_FAILED) err("mmap mmap1"); close(fd1); unlink("mmap1"); int fd2; if((fd2 = open("mmap2", O_RDWR|O_CREATE)) < 0) err("open mmap2"); if(write(fd2, "67890", 5) != 5) err("write mmap2"); char *p2 = mmap(0, PGSIZE, PROT_READ, MAP_PRIVATE, fd2, 0); if(p2 == MAP_FAILED) err("mmap mmap2"); close(fd2); unlink("mmap2"); if(memcmp(p1, "12345", 5) != 0) err("mmap1 mismatch"); if(memcmp(p2, "67890", 5) != 0) err("mmap2 mismatch"); if (munmap(p1, PGSIZE) == -1) err("munmap (5)"); if(memcmp(p2, "67890", 5) != 0) err("mmap2 mismatch (2)"); if (munmap(p2, PGSIZE) == -1) err("munmap (6)"); printf("test mmap two files: OK\n"); printf("mmap_test: ALL OK\n"); } // // mmap a file, then fork. // check that the child sees the mapped file. // void fork_test(void) { int fd; int pid; const char * const f = "mmap.dur"; printf("fork_test starting\n"); testname = "fork_test"; // mmap the file twice. makefile(f); if ((fd = open(f, O_RDONLY)) == -1) err("open"); unlink(f); char *p1 = mmap(0, PGSIZE*2, PROT_READ, MAP_SHARED, fd, 0); if (p1 == MAP_FAILED) err("mmap (4)"); char *p2 = mmap(0, PGSIZE*2, PROT_READ, MAP_SHARED, fd, 0); if (p2 == MAP_FAILED) err("mmap (5)"); // read just 2nd page. if(*(p1+PGSIZE) != 'A') err("fork mismatch (1)"); if((pid = fork()) < 0) err("fork"); if (pid == 0) { _v1(p1); if (munmap(p1, PGSIZE) == -1) // just the first page err("munmap (7)"); exit(0); // tell the parent that the mapping looks OK. } int status = -1; wait(&status); if(status != 0){ printf("fork_test failed\n"); exit(1); } // check that the parent's mappings are still there. _v1(p1); _v1(p2); printf("fork_test OK\n"); }