项目概述
你需要在 proc.c
、proc.h
、sysfile.c
和 trap.c
文件中实现特定功能,主要关注这些文件中的 "TODO" 部分。你的项目评分将仅基于这些 "TODO" 部分的实现,修改其他部分的代码是无效的。
关键步骤
-
阅读并理解介绍部分:
- 带有 (*) 标记的部分是介绍部分。这些部分介绍了工具、函数和基本概念,帮助你理解项目要求。
- 重点理解每个函数的工作原理以及它们与系统的交互方式。这些函数可能需要在实现 TODO 部分时使用。
-
找到并学习测试程序:
- 在
csc3150-project3/user
目录中找到mmaptest.c
,这是测试你实现功能的入口文件。 - 通过学习
mmaptest.c
,你可以看到你的代码将如何被测试,理解你实现的功能应该具备的预期行为。
- 在
-
只实现 TODO 部分:
- 在
proc.c
、proc.h
、sysfile.c
和trap.c
中查找 "TODO" 注释。这些是你唯一可以修改的部分。 - 每个 TODO 部分通常包含注释,描述该函数的目的、输入、输出以及特定的逻辑或操作顺序。基于这些描述以及介绍部分中的 API 或函数,完成你的代码实现。
- 在
-
(可选)深入了解 xv6 系统:
- 如果你希望深入了解 xv6 系统,可以参考 xv6-book 以获取更详细的信息。
- 这本书可以在此处获取,它提供了关于 xv6 设计和内部实现的详细信息。
重要提示总结
- 仅限修改 指定四个文件中的 TODO 部分。
- 学习
mmaptest.c
以理解你的实现将如何被测试。 - 理解介绍部分 以了解需要的工具和函数。
- 不在 TODO 之外的修改 将不被评分,因此确保所有更改都在指定的 TODO 区域内。
mmaptest.c
cpp
#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];
char buff[PGSIZE * 2];
#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");
munmap(p1, PGSIZE);
if(memcmp(p2, "67890", 5) != 0)
err("mmap2 mismatch (2)");
munmap(p2, PGSIZE);
printf("test mmap two files: OK\n");
//
// Check offset argument to mmap.
//
printf("test mmap offset\n");
// Fill the buffer with 'A' for the first page and 'B' for the second page
for (i = 0; i < PGSIZE; i++) {
buff[i] = 'A';
buff[i + PGSIZE] = 'B';
}
// Create and open the file
int fd3;
if((fd3 = open("mmap3", O_RDWR | O_CREATE)) < 0)
err("open mmap3");
// Write the buffer to the file
if (write(fd3, buff, sizeof(buff)) != sizeof(buff)) {
err("write mmap3");
}
p = mmap(0, PGSIZE, PROT_READ, MAP_PRIVATE, fd3, PGSIZE);
if(p == MAP_FAILED)
err("mmap offset");
if(*p != 'B')
err("mmap offset mismatch");
munmap(p, PGSIZE);
close(fd3);
unlink("mmap3");
printf("test mmap offset: OK\n");
// Check that the half page file is mapped correctly
printf("test mmap half page\n");
int fd4;
if((fd4 = open("mmap4", O_RDWR | O_CREATE)) < 0)
err("open mmap4");
if(write(fd4, buff, PGSIZE) != PGSIZE)
err("write mmap4");
char *p4 = mmap(0, PGSIZE / 2, PROT_READ, MAP_PRIVATE, fd4, 0);
if(p4 == MAP_FAILED)
err("mmap half page");
char *p5 = mmap(0, PGSIZE, PROT_READ, MAP_PRIVATE, fd4, 0);
if(p5 == MAP_FAILED)
err("mmap page");
if((p5-p4)%PGSIZE)
err("page alignment mismatch");
munmap(p5, PGSIZE);
munmap(p4, PGSIZE / 2);
close(fd4);
unlink("mmap4");
printf("test mmap half page: 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);
munmap(p1, PGSIZE); // just the first page
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");
}
文件结构和测试函数概览
mmap_test()
:主要测试mmap
和munmap
的不同功能,包括文件映射、权限控制、偏移映射等。fork_test()
:测试mmap
在子进程中的继承情况,确保在fork
后子进程能够访问父进程映射的内容。
具体测试分析
1. makefile(const char *f)
- 作用:创建一个包含特定内容的文件(1.5 页的
A
字符和半页的零)。 - 目的:测试
mmap
映射的内容是否与文件内容一致。
2. mmap_test()
- 作用 :测试
mmap
的核心功能和一些特定场景。 - 关键测试场景 :
- 基础映射:将文件映射到内存并检查内容是否匹配。
- 私有映射 :测试
MAP_PRIVATE
模式,确保修改内容不会影响文件。 - 读写权限:测试只读文件是否禁止读写映射,读写文件是否允许读写映射。
- 偏移映射:测试映射文件时的偏移量是否正确处理。
- 多文件映射:同时映射两个文件并检查内容,确保映射是独立的。
- 非整页映射:测试映射文件的一部分(如半页)是否正确。
3. fork_test()
- 作用 :测试
mmap
在fork
后的行为,确保子进程能够访问到映射的内存内容。 - 关键点 :父进程
mmap
一个文件后,子进程通过fork
继承该映射,检查子进程是否可以正确读取数据,验证MAP_SHARED
的共享效果。