[OS] 项目3入手

项目概述

你需要在 proc.cproc.hsysfile.ctrap.c 文件中实现特定功能,主要关注这些文件中的 "TODO" 部分。你的项目评分将仅基于这些 "TODO" 部分的实现,修改其他部分的代码是无效的。

关键步骤

  1. 阅读并理解介绍部分

    • 带有 (*) 标记的部分是介绍部分。这些部分介绍了工具、函数和基本概念,帮助你理解项目要求。
    • 重点理解每个函数的工作原理以及它们与系统的交互方式。这些函数可能需要在实现 TODO 部分时使用。
  2. 找到并学习测试程序

    • csc3150-project3/user 目录中找到 mmaptest.c,这是测试你实现功能的入口文件。
    • 通过学习 mmaptest.c,你可以看到你的代码将如何被测试,理解你实现的功能应该具备的预期行为。
  3. 只实现 TODO 部分

    • proc.cproc.hsysfile.ctrap.c 中查找 "TODO" 注释。这些是你唯一可以修改的部分。
    • 每个 TODO 部分通常包含注释,描述该函数的目的、输入、输出以及特定的逻辑或操作顺序。基于这些描述以及介绍部分中的 API 或函数,完成你的代码实现。
  4. (可选)深入了解 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() :主要测试 mmapmunmap 的不同功能,包括文件映射、权限控制、偏移映射等。
  • fork_test() :测试 mmap 在子进程中的继承情况,确保在 fork 后子进程能够访问父进程映射的内容。

具体测试分析

1. makefile(const char *f)
  • 作用:创建一个包含特定内容的文件(1.5 页的 A 字符和半页的零)。
  • 目的:测试 mmap 映射的内容是否与文件内容一致。
2. mmap_test()
  • 作用 :测试 mmap 的核心功能和一些特定场景。
  • 关键测试场景
    • 基础映射:将文件映射到内存并检查内容是否匹配。
    • 私有映射 :测试 MAP_PRIVATE 模式,确保修改内容不会影响文件。
    • 读写权限:测试只读文件是否禁止读写映射,读写文件是否允许读写映射。
    • 偏移映射:测试映射文件时的偏移量是否正确处理。
    • 多文件映射:同时映射两个文件并检查内容,确保映射是独立的。
    • 非整页映射:测试映射文件的一部分(如半页)是否正确。
3. fork_test()
  • 作用 :测试 mmapfork 后的行为,确保子进程能够访问到映射的内存内容。
  • 关键点 :父进程 mmap 一个文件后,子进程通过 fork 继承该映射,检查子进程是否可以正确读取数据,验证 MAP_SHARED 的共享效果。
相关推荐
23级二本计科5 分钟前
Linux信号_信号的产生
linux·运维·服务器
ErvinHowell6 分钟前
文件MD5生成性能大提升!如何实现分片与Worker优化
前端·vue.js·算法
微服务技术分享8 分钟前
专为成长型企业打造的Java CRM系统源码:CRM客户关系管理系统技术解析与功能构建
java·crm客户关系管理系统源码·鸿鹄crm客户关系管理系统·鸿鹄crm客户关系管理系统源码
琪露诺大湿9 分钟前
JavaEE-多线程初阶(4)
java·开发语言·jvm·java-ee·基础·1024程序员节·原神
Java程序员-小白17 分钟前
Spring Shell——快速构建终端应用,自定义终端命令
java·后端·spring
想做白天梦22 分钟前
LeetCode :150. 逆波兰表达式求值(含求后缀表达式和中缀转后缀表达式)
java·前端·算法
远望樱花兔26 分钟前
【d63】【Java】【力扣】141.训练计划III
java·开发语言·leetcode
九圣残炎28 分钟前
【从零开始的LeetCode-算法】3254. 长度为 K 的子数组的能量值 I
java·算法·leetcode
北京迅为1 小时前
【北京迅为】《STM32MP157开发板嵌入式开发指南》-第七十八章 Qt控制硬件
linux·stm32·单片机·嵌入式硬件
那你为何对我三笑留情1 小时前
六、Spring Boot集成Spring Security之前后分离项目认证流程最佳方案
java·spring boot·分布式·后端·spring·spring security