xv6项目开源—05

news/2024/7/10 19:09:06 标签: 开源

xv6项目开源—05.md

理论:

1、设备驱动程序在两种环境中执行代码:上半部分在进程的内核线程中运行,下半部分在中断时执行。上半部分通过系统调用进行调用,如希望设备执行I/O操作的readwrite。这段代码可能会要求硬件执行操作(例如,要求磁盘读取块);然后代码等待操作完成。最终设备完成操作并引发中断。驱动程序的中断处理程序充当下半部分,计算出已经完成的操作,如果合适的话唤醒等待中的进程,并告诉硬件开始执行下一个正在等待的操作。

2、驱动程序管理的UART硬件是由QEMU仿真的16550芯片。在真正的计算机上,16550将管理连接到终端或其他计算机的RS232串行链路。运行QEMU时,它连接到键盘和显示器。

3、xv6的shell通过*init.c* (*user/init.c*:19)中打开的文件描述符从控制台读取输入。对read的调用实现了从内核流向consoleread (*kernel/console.c*:82)的数据通路。consoleread等待输入到达(通过中断)并在cons.buf中缓冲,将输入复制到用户空间,然后(在整行到达后)返回给用户进程。如果用户还没有键入整行,任何读取进程都将在sleep系统调用中等待(kernel/console.c:98)

4、计时器中断可能发生在用户或内核代码正在执行的任何时候;内核无法在临界区操作期间禁用计时器中断。因此,计时器中断处理程序必须保证不干扰中断的内核代码。基本策略是处理程序要求RISC-V发出“软件中断”并立即返回。RISC-V用普通陷阱机制将软件中断传递给内核,并允许内核禁用它们。处理由定时器中断产生的软件中断的代码可以在devintr (*kernel/trap.c*:204)中看到

5、UART驱动程序首先将传入的数据复制到内核中的缓冲区,然后复制到用户空间。这在低数据速率下是可行的,但是这种双重复制会显著降低快速生成或消耗数据的设备的性能。一些操作系统能够直接在用户空间缓冲区和设备硬件之间移动数据,通常带有DMA。

6、延迟分配用户空间堆内存(lazy allocation of user-space heap memory)

实践:

1、Lazy allocation

​ 修改***trap.c***中的代码以响应来自用户空间的页面错误,方法是新分配一个物理页面并映射到发生错误的地址,然后返回到用户空间,让进程继续执行。您应该在生成“usertrap(): …”消息的printf调用之前添加代码。你可以修改任何其他xv6内核代码,以使echo hi正常工作。

​ 这个实验很简单,就仅仅改动sys_sbrk()函数即可,将实际分配内存的函数删除,而仅仅改变进程的sz属性

uint64
sys_sbrk(void)
{
  int addr;
  int n;

  if(argint(0, &n) < 0)
    return -1;

  addr = myproc()->sz;
  // lazy allocation
  myproc()->sz += n;

  return addr;
}

(1). 修改usertrap()(*kernel/trap.c*)函数,使用r_scause()判断是否为页面错误,在页面错误处理的过程中,先判断发生错误的虚拟地址(r_stval()读取)是否位于栈空间之上,进程大小(虚拟地址从0开始,进程大小表征了进程的最高虚拟地址)之下,然后分配物理内存并添加映射

  uint64 cause = r_scause();
  if(cause == 8) {
    ...
  } else if((which_dev = devintr()) != 0) {
    // ok
  } else if(cause == 13 || cause == 15) {
    // 处理页面错误
    uint64 fault_va = r_stval();  // 产生页面错误的虚拟地址
    char* pa;                     // 分配的物理地址
    if(PGROUNDUP(p->trapframe->sp) - 1 < fault_va && fault_va < p->sz &&
      (pa = kalloc()) != 0) {
        memset(pa, 0, PGSIZE);
        if(mappages(p->pagetable, PGROUNDDOWN(fault_va), PGSIZE, (uint64)pa, PTE_R | PTE_W | PTE_X | PTE_U) != 0) {
          kfree(pa);
          p->killed = 1;
        }
    } else {
      // printf("usertrap(): out of memory!\n");
      p->killed = 1;
    }
  } else {
    ...
  }

(2). 修改uvmunmap()(*kernel/vm.c*),之所以修改这部分代码是因为lazy allocation中首先并未实际分配内存,所以当解除映射关系的时候对于这部分内存要略过,而不是使系统崩溃,这部分在课程视频中已经解答。

void
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{
  ...

  for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
    if((pte = walk(pagetable, a, 0)) == 0)
      panic("uvmunmap: walk");
    if((*pte & PTE_V) == 0)
      continue;

    ...
  }
}

2、Lazytests and Usertests

​ 我们为您提供了lazytests,这是一个xv6用户程序,它测试一些可能会给您的惰性内存分配器带来压力的特定情况。修改内核代码,使所有lazytestsusertests都通过。

  • 处理sbrk()参数为负的情况。
  • 如果某个进程在高于sbrk()分配的任何虚拟内存地址上出现页错误,则终止该进程。
  • fork()中正确处理父到子内存拷贝。
  • 处理这种情形:进程从sbrk()向系统调用(如readwrite)传递有效地址,但尚未分配该地址的内存。
  • 正确处理内存不足:如果在页面错误处理程序中执行kalloc()失败,则终止当前进程。
  • 处理用户栈下面的无效页面上发生的错误。

(1). 处理sbrk()参数为负数的情况,参考之前sbrk()调用的growproc()程序,如果为负数,就调用uvmdealloc()函数,但需要限制缩减后的内存空间不能小于0

uint64
sys_sbrk(void)
{
  int addr;
  int n;

  if(argint(0, &n) < 0)
    return -1;

  struct proc* p = myproc();
  addr = p->sz;
  uint64 sz = p->sz;

  if(n > 0) {
    // lazy allocation
    p->sz += n;
  } else if(sz + n > 0) {
    sz = uvmdealloc(p->pagetable, sz, sz + n);
    p->sz = sz;
  } else {
    return -1;
  }
  return addr;
}

(2). 正确处理fork的内存拷贝:fork调用了uvmcopy进行内存拷贝,所以修改uvmcopy如下

int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
  ...
  for(i = 0; i < sz; i += PGSIZE){
    if((pte = walk(old, i, 0)) == 0)
      continue;
    if((*pte & PTE_V) == 0)
      continue;
    ...
  }
  ...
}

(3). 还需要继续修改uvmunmap,否则会运行出错

void
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{
  ...

  for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
    if((pte = walk(pagetable, a, 0)) == 0)
      continue;
    if((*pte & PTE_V) == 0)
      continue;

    ...
  }
}

(4). 处理通过sbrk申请内存后还未实际分配就传给系统调用使用的情况,系统调用的处理会陷入内核,scause寄存器存储的值是8,如果此时传入的地址还未实际分配,就不能走到上文usertrap中判断scause是13或15后进行内存分配的代码,syscall执行就会失败

  • 系统调用流程:
    • 陷入内核**>usertrapr_scause()==8的分支>syscall()==>**回到用户空间
  • 页面错误流程:
    • 陷入内核**>usertrapr_scause()==13||r_scause()==15的分支>分配内存==>**回到用户空间

因此就需要找到在何时系统调用会使用这些地址,将地址传入系统调用后,会通过argaddr函数(*kernel/syscall.c*)从寄存器中读取,因此在这里添加物理内存分配的代码

int
argaddr(int n, uint64 *ip)
{
  *ip = argraw(n);
  struct proc* p = myproc();

  // 处理向系统调用传入lazy allocation地址的情况
  if(walkaddr(p->pagetable, *ip) == 0) {
    if(PGROUNDUP(p->trapframe->sp) - 1 < *ip && *ip < p->sz) {
      char* pa = kalloc();
      if(pa == 0)
        return -1;
      memset(pa, 0, PGSIZE);

      if(mappages(p->pagetable, PGROUNDDOWN(*ip), PGSIZE, (uint64)pa, PTE_R | PTE_W | PTE_X | PTE_U) != 0) {
        kfree(pa);
        return -1;
      }
    } else {
      return -1;
    }
  }

  return 0;
}


http://www.niftyadmin.cn/n/5471754.html

相关文章

学习 Git 基础知识 - 日常开发任务手册

欢迎来到我关于 Git 的综合指南&#xff0c;Git 是一种分布式版本控制系统&#xff0c;已经在软件开发中彻底改变了协作和代码管理方式。 无论你是经验丰富的开发者还是刚开始编程之旅的新手&#xff0c;理解 Git 对于正确掌控代码、高效管理项目和与他人合作至关重要。 在本…

npm安装时一直idealTree:npm: sill idealTree buildDeps解决方案

1-设置为淘宝的镜像源&#xff1a; npm config set registry https://registry.npm.taobao.org 使用如下命令检验是否成功&#xff1a; npm config get registry 输出淘宝镜像路径 继续运行 npm install

Linux mattrib命令教程:如何管理MS-DOS文件的属性(附实例详解和注意事项)

Linux mattrib命令介绍 mattrib 是一个用于更改MS-DOS文件属性标志的命令。它可以添加属性标志到一个MS-DOS文件&#xff08;使用 操作符&#xff09;或者移除属性标志&#xff08;使用 - 操作符&#xff09;。 Linux mattrib命令适用的Linux版本 mattrib 命令在大多数Linu…

[实时流基础 flink] 窗口函数

尚硅谷学习笔记 6.5 窗口函数 增量聚合函数&#xff08;ReduceFunction / AggregateFunction&#xff09; 窗口将数据收集起来&#xff0c;最基本的处理操作当然就是进行聚合。我们可以每来一个数据就在之前结果上聚合一次&#xff0c;这就是“增量聚合”。 典型的增量聚合函数…

蓝桥杯-蜗牛[Java]

目录 问题描述: 输入格式: 输出格式: 样例输入: 样例输出: 样例说明: 评测用例规模与约定: 分析: 代码: 问题描述: 这天&#xff0c;一只蜗牛来到了二维坐标系的原点。 在 x 轴上长有 n 根竹竿。它们平行于 y 轴&#xff0c;底部纵坐标为 0 &#xff0c;横坐标分别为 …

利用nginx-http-flv-module实现三种直播

目录 一、说明 二、目标 三、实现 四、直播地址 一、说明 此文在《流媒体服务器的搭建(支持hls)》《搭建nginx-http-flv-module直播系统》之后编写,很多详细内容需要参考它。 流媒体服务器的搭建(支持hls)

Thinkphp5萤火商城B2C小程序源码

源码介绍 Thinkphp5萤火商城B2C小程序源码&#xff0c;是一款开源的电商系统&#xff0c;为中小企业提供最佳的新零售解决方案。采用稳定的MVC框架开发&#xff0c;执行效率、扩展性、稳定性值得信赖。 环境要求 Nginx/Apache/IIS PHP5.4 MySQL5.1 建议使用环境&#xff…

Docker-compose部署 gitlab-server

Gitlab部署文档 1、配置环境变量 export GITLAB_HOME/srv/gitlab2、创建docker-compose.yml文件 修改http端口需要同步修改external_url修改ssh端口需要同步修改gitlab_rails[‘gitlab_shell_ssh_port’]volumes&#xff1a;容器数据的持久化存储&#xff0c;强烈推荐。除非你…