6.s081操作系统Lab4: trap

news2024/12/23 18:27:05

文章目录

  • chapter 4
    • 概览
    • 4.1 CPU trap流程
      • 使用寄存器
      • 如果cpu想处理1个trap
    • 4.2 用户态引发的trap
      • 4.2.1 uservec
      • 4.2.2 usertrap
      • 4.2.3 usertrapret和userret
        • usertrapret
        • userret
  • Lab4
    • Backtrace (moderate)
    • Alarm (hard)

chapter 4

概览

  1. trap的场景:系统调用,设备中断,异常
  2. trap对用户是透明的,用户不会察觉发生了1个trap:内核会保存trap前的状态,在trap后恢复

4.1 CPU trap流程

使用寄存器

stvec: 保存trap程序地址
sepc: 临时保存pc寄存器,trap结束时,sret(TODO 不知道是什么,可能是一段程序)会重新将sepc复杂到pc中
scause: trap原因
sscratch: 方便上下文切换

  1. 见userret,sscratch寄存器保存用户页表的trapframe
  2. 见uservec,trapframe页可以用来暂存用户态的寄存器,中断后切换回来;同时保存内核页表在中断时从用户页表切换到内核页表,可以认为是个中介的临时仓库

sstatus: SPP表示从用户态(0)或从内核态(1)切换过来的trap;SIE表示是否启用设备中断

如果cpu想处理1个trap

trap相关:设置scause和sstatus,保存trap原因和来源
状态保存相关:把pc暂存到sepc
执行相关:切换到监督者模式,把stvec复制到pc
cpu不会切换内核页表,不会切换内核栈。但是必须切换pc。

4.2 用户态引发的trap

4.2.1 uservec

uservec就是用户态的trap入口,即cpu的stvec会被设成uservec。
这里要完成3个事:

  1. 保存用户态的32个寄存器
  2. 切换satp寄存器,使用内核页表
  3. 调用处理中断的函数usertrap

(倒叙,写用户进程开始执行前的事情,可参见4.2.3节usertrapret和userret的功能)
在进入用户空间之前,内核会分配1页TRAPFRAME,专门用来暂存trap发生时需要的东西,这个TRAPFRAME的地址放在sscratch寄存器中,TRAPFRAME页还会预先放着开始就已经知道且在trap发生时需要用到的东西:usertrap的地址(进行trap类型判断并调用相应处理函数)、cpu的hartid(TODO,还不知道作用,可能是CPU的id,可以记录处理trap的CPU)、内核页表地址(uservec需要进行用户态页表到内核态页表的切换)。

.globl uservec
uservec:    
	#
        # trap.c sets stvec to point here, so
        # traps from user space start here,
        # in supervisor mode, but with a
        # user page table.
        #
        # sscratch points to where the process's p->trapframe is
        # mapped into user space, at TRAPFRAME.
        #
        
	# swap a0 and sscratch
        # so that a0 is TRAPFRAME
        csrrw a0, sscratch, a0

        # save the user registers in TRAPFRAME
        sd ra, 40(a0)
        sd sp, 48(a0)
        sd gp, 56(a0)
        .............

	# save the user a0 in p->trapframe->a0
        csrr t0, sscratch
        sd t0, 112(a0)

        # restore kernel stack pointer from p->trapframe->kernel_sp
        ld sp, 8(a0)

        # make tp hold the current hartid, from p->trapframe->kernel_hartid
        ld tp, 32(a0)

        # load the address of usertrap(), p->trapframe->kernel_trap
        ld t0, 16(a0)

        # restore kernel page table from p->trapframe->kernel_satp
        ld t1, 0(a0)
        csrw satp, t1
        sfence.vma zero, zero

        # a0 is no longer valid, since the kernel page
        # table does not specially map p->tf.

        # jump to usertrap(), which does not return
        jr t0

4.2.2 usertrap

usertrap函数会处理来自用户态的中断、异常或系统调用,由uservec汇编代码调用;这里会判断trap的原因,以调用合适的处理函数。最后调用usertrapret()返回用户态。

//
// handle an interrupt, exception, or system call from user space.
// called from trampoline.S
//
void
usertrap(void)
{
  int which_dev = 0;

  if((r_sstatus() & SSTATUS_SPP) != 0)
    panic("usertrap: not from user mode");

  // send interrupts and exceptions to kerneltrap(),
  // since we're now in the kernel.
  w_stvec((uint64)kernelvec);

  struct proc *p = myproc();
  
  // save user program counter.
  p->trapframe->epc = r_sepc();
  
  if(r_scause() == 8){
    // system call

    if(p->killed)
      exit(-1);

    // sepc points to the ecall instruction,
    // but we want to return to the next instruction.
    p->trapframe->epc += 4;

    // an interrupt will change sstatus &c registers,
    // so don't enable until done with those registers.
    intr_on();

    syscall();
  } else if((which_dev = devintr()) != 0){
    // ok
  } else {
    printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
    printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
    p->killed = 1;
  }

  if(p->killed)
    exit(-1);

  // give up the CPU if this is a timer interrupt.
  if(which_dev == 2)
    yield();

  usertrapret();
}

4.2.3 usertrapret和userret

usertrapret

usertrapret:切换pc寄存器
userret:恢复寄存器,切换页表
usertrapret代码如下,

  1. 临时关闭中断功能:
    intr_off();将中断开关临时关闭(TODO:如何关闭),在从内核态到用户态的转换过程中,暂时停止中断功能,等切换完毕后再开启,可能是为了避免状态机紊乱。
  2. 改变 stvec 来引用 uservec:
    w_stvec(TRAMPOLINE + (uservec - trampoline));推测是重新写cpu的stvec寄存器为uservec地址,以保证下次中断时,cpu仍然跳转到uservec 去处理中断。
  3. 准备 uservec 所依赖的 trapframe 字段,如kernel_satp为内核页表地址等等。
  4. 写一些CPU寄存器:如设sstatus的SPP为0,表示为用户态的中断;设sstatus的SPIE为1,表示在用户态使能中断
  5. 将 sepc 设置为先前保存的用户程序计数器w_sepc(p->trapframe->epc);
  6. 调用 userret,并把TRAPFRAMEsatp作为参数传递过去,userret会切换用户态页表,重设用户态寄存器,最后切换回用户态
//
// return to user space
//
void
usertrapret(void)
{
  struct proc *p = myproc();

  // we're about to switch the destination of traps from
  // kerneltrap() to usertrap(), so turn off interrupts until
  // we're back in user space, where usertrap() is correct.
  intr_off();

  // send syscalls, interrupts, and exceptions to trampoline.S
  w_stvec(TRAMPOLINE + (uservec - trampoline));

  // set up trapframe values that uservec will need when
  // the process next re-enters the kernel.
  p->trapframe->kernel_satp = r_satp();         // kernel page table
  p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
  p->trapframe->kernel_trap = (uint64)usertrap;
  p->trapframe->kernel_hartid = r_tp();         // hartid for cpuid()

  // set up the registers that trampoline.S's sret will use
  // to get to user space.
  
  // set S Previous Privilege mode to User.
  unsigned long x = r_sstatus();
  x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
  x |= SSTATUS_SPIE; // enable interrupts in user mode
  w_sstatus(x);

  // set S Exception Program Counter to the saved user pc.
  w_sepc(p->trapframe->epc);

  // tell trampoline.S the user page table to switch to.
  uint64 satp = MAKE_SATP(p->pagetable);

  // jump to trampoline.S at the top of memory, which 
  // switches to the user page table, restores user registers,
  // and switches to user mode with sret.
  uint64 fn = TRAMPOLINE + (userret - trampoline);
  ((void (*)(uint64,uint64))fn)(TRAPFRAME, satp);
}
userret
  1. 将 satp 切换到进程的用户页表,因为用户态和内核态的trampoline都是直接映射,因此在此时进行页表切换后,trampoline的程序仍能继续往下执行。此时a0寄存器指向用户页表的TRAPFRAME页,先将其保存到sscratch
.globl userret
userret:
        # userret(TRAPFRAME, pagetable)
        # switch from kernel to user.
        # usertrapret() calls here.
        # a0: TRAPFRAME, in user page table.
        # a1: user page table, for satp.

        # switch to the user page table.
        csrw satp, a1
        sfence.vma zero, zero

        # put the saved user a0 in sscratch, so we
        # can swap it with our a0 (TRAPFRAME) in the last step.
        ld t0, 112(a0)
        csrw sscratch, t0

        # restore all but a0 from TRAPFRAME
        ld ra, 40(a0)
        ld sp, 48(a0)
        ld gp, 56(a0)
        。。。。

	# restore user a0, and save TRAPFRAME in sscratch
        csrrw a0, sscratch, a0
        
        # return to user mode and user pc.
        # usertrapret() set up sstatus and sepc.
        sret

Lab4

Backtrace (moderate)

实验内容:添加栈帧信息打印
考察点:xv6的栈结构;栈以类似链表的形式保存在1个页面中
关键提示:address lives at a fixed offset (-8) from the frame pointer of a stackframe, and that the saved frame pointer lives at fixed offset (-16) from the frame pointer.
在这里插入图片描述

关键代码:

void
backtrace(void)
{
  printf("backtrace:\n");
  uint64 fp = r_fp();
  uint64 down = PGROUNDDOWN(fp);
  uint64 up = PGROUNDUP(fp);
  while (fp >= down && fp < up)
  {
    uint64* res_addr = (uint64*)(fp - 8);
    uint64* next_fp_addr = (uint64*)(fp - 16);
    printf("%p\n", *res_addr);
    fp = *next_fp_addr;
  }
}

Alarm (hard)

实验内容:实现系统调用,在进程使用CPU时间超时时,进行回调函数调用,并能正常返回用户态
考察点:系统调用流程;usertrap的寄存器保存位置在trapframe页面;usertrap的pc计数器存储在epc寄存器;
关键提示:

  • When a trap on the RISC-V returns to user space, what determines the instruction address at which user-space code resumes execution?
  • Your solution will require you to save and restore registers—what registers do you need to save and restore to resume the interrupted code correctly? (Hint: it will be many).

关键代码:

// kernel/sysproc.c
int
sys_sigreturn(void)
{
  memmove(myproc()->trapframe, myproc()->trapframe_back, sizeof(struct trapframe));
  myproc()->calling = 0;
  return 0;
}
//kernel/trap.c
  // give up the CPU if this is a timer interrupt.
  if(which_dev == 2)
  {
    p->ticks_count ++;
    if (p->alarmInterval != -1 && p->ticks_count >= p->alarmInterval && p->calling != 1)
    {
      // if a handler hasn't returned yet, the kernel shouldn't call it again
      p->calling = 1;
      //"re-arm" the alarm counter after each time it goes off
      p->ticks_count = 0;
      //save and restore registers
      memmove(p->trapframe_back, p->trapframe, sizeof(struct trapframe));
      //Q:When a trap on the RISC-V returns to user space,
      //what determines the instruction address at which user-space code resumes execution?
      //A: epc!
      p->trapframe->epc = p->alarmHandler;
    }
    yield();
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1320322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity3D拆分模型动画展示

系列文章目录 Unity工具 文章目录 系列文章目录前言一、模型拆分功能1-1、首先先搭建一个简单的场景1-2、导入DoTween插件1-3、代码实现1-4、效果展示&#xff08;一个一个拆分的&#xff09; 二、对称模型拆分2-1、规则的&#xff0c;镜像的&#xff0c;对称的都可以使用2-2、…

gitee提交代码步骤介绍(含git环境搭建)

1、gitee官网地址 https://gitee.com; 2、Windows中安装git环境 参考博客&#xff1a;《Windows中安装Git软件和TortoiseGit软件》&#xff1b; 3、设置用户名和密码 这里的用户名和密码就是登录gitee网站的用户名和密码如果设置错误&#xff0c;可以在Windows系统的“凭据管理…

八股文打卡day3——计算机网络(3)

面试题&#xff1a;请讲一下四次挥手的过程&#xff1f; 1.客户端发送FIN数据包给服务器&#xff0c;表示客户端不再发送数据给服务器&#xff0c;想要断开这个方向的连接。 2.服务器收到客户端的FIN包之后&#xff0c;发送ACK包给客户端&#xff0c;对收到的FIN包进行收到确认…

你也是爬虫高手?毫秒级抢票,光速入监狱

文章目录 前言案情介绍法院观点判决情况判决文书案例分析 关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 前言 …

C++ 开发中为什么要使用继承

为何继承 实验介绍 继承是 C++ 中的特性之一,使用继承能够有效减轻工作量,使得开发更加高效。 知识点 什么是继承为何继承继承的内容权限关键字什么是继承 生活中继承是指孩子继承父亲的财产等。C++ 使用了这一思想,却又与生活中的继承不一样。 在使用继承时,派生类是…

cesium学习笔记(问题记录)——(三)

一、根据点跟角度计算另一点坐标&#xff08;三维球体&#xff09; export const getAnotherPoint (lon: number, lat: number, angle: number, distance: number) > {// WGS84坐标系var a 6378137; // 赤道半径var b 6356752.3142; // 短半径var f 1 / 298.257223563;…

valgrind定位C++线程/内存等错误

Valgrind 是一套 Linux 下&#xff0c;开放源代码&#xff08;GPL V2&#xff09;的仿真调试工具的集合。 Valgrind 由内核&#xff08;core&#xff09;以及基于内核的其他调试工具组成。内核类似于一个框架&#xff08;framework&#xff09;&#xff0c;它模拟了一个 CPU 环…

【漏洞复现】CVE-2023-47261 Dokmee ECM信息泄露致远程命令执行

漏洞描述 Dokmee ECM是一款国外企业内容管理 (ECM) 软件。每个公司的办公室每个角落都存放着文档、记录和档案。Dokmee 一系列解决方案可以帮助您高效地组织、保护和管理这些文件。支持的文件:PDF、TIFF、Word、Excel、Auto-CAD 绘图、电子邮件等。Dokmee 可以帮助您立即实现…

模型部署之模型转换

一、模型转换的意义 模型部署是为了模型能在不同框架间流转。 在实际应用时&#xff0c;模型转换几户都用于工业部署&#xff0c;负责模型从训练框架到部署侧推理框架的连接&#xff0c;这是因为随着深度学习随着深度学习应用和技术的演进&#xff0c;训练框架和推理框架的职…

在linux上基于shell自动部署Java项目

一&#xff0c;安装git yum list git 列出git安装包 yum install git 在线安装git 使用 git -varsion 查看是否安装成功 安装成功 二&#xff0c; Git克隆代码 git clone 远程仓库地址 三&#xff0c;创建shell脚本 touch shell.sh shell脚本 #!/bin/sh echo echo 自动…

1130 - Host “WIN-CA4FHERGO9J‘ is not allowed to connect to this MySQL server

1、知识小课堂 1.1 Mysql MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB公司开发&#xff0c;属于Oracle旗下产品。它是最流行的关系型数据库管理系统之一&#xff0c;在WEB应用方面&#xff0c;MySQL是最好的RDBMS (Relational Database Management System&am…

详解 Jeecg-boot 框架如何配置 elasticsearch

目录 一、下载安装 Elasticsearch 1、 地址&#xff1a;https://www.elastic.co/cn/downloads/elasticsearch 2、下载完成后&#xff0c;解压缩&#xff0c;进入config目录更改配置文件 3、 修改配置完成后&#xff0c;前往bin目录启动el 4、访问&#xff1a;localhost:92…

Java对象结构

Java 对象(Object 实例)结构包括三部分:对象头、对象体、对齐字节。 Object的三个部分 对象头包括三个字段&#xff0c;第一个字段叫做 Mark Word(标记字)&#xff0c;用于存储自身运行时的数据 例如 GC 标志位、哈希码、锁状态等信息。 第二个字段叫做 Class Pointer(类对象…

排序算法——归并排序

void print_arr(int arr[], int n) {for (int i 0;i < n;i){printf("%d", arr[i]);}putchar("\n"); }//合并&#xff08;归并排序最主要的部分&#xff09; void merge(int arr[], int tempArr[],int left,int mid,int right) {//标记左半区第一个未排…

模型部署概述

一、前言 一般来说&#xff0c;学术界负责各种 SOTA(State of the Art) 模型的训练和结构探索&#xff0c;而工业界负责将这些 SOTA 模型应用落地&#xff0c;赋能百业。本文将要讲述的是&#xff0c;在 CV 场景中&#xff0c;如何实现模型的快速落地&#xff0c;赋能到产业应…

【PWN】学习笔记(三)【返回导向编程】(下)

目录 课程回顾ret2libc![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ebe1a9a9e54f4319946621dbe89c5774.png)做题 ret2libc2ret2libc3 课程 课程链接&#xff1a;https://www.bilibili.com/video/BV1854y1y7Ro/?vd_source7b06bd7a9dd90c45c5c9c44d12e7b4e6 课程…

Redis Set类型

集合类型也是保存多个字符串类型的元素的&#xff0c;但和列表类型不同的是&#xff0c;集合中 1&#xff09;元素之间是无序的 2&#xff09;元素不允许重复 一个集合中最多可以存储2的32次方个元素。Redis 除了支持集合内的增删查改操作&#xff0c;同时还支持多个集合取交…

利用python进行数据分析 第十四章 数据分析案例

本书正文的最后一章&#xff0c;我们来看一些真实世界的数据集。对于每个数据集&#xff0c;我们会用之前介绍的方 法&#xff0c;从原始数据中ᨀ 取有意义的内容。展示的方法适用于其它数据集&#xff0c;也包括你的。本章包含了一 些各种各样的案例数据集&#xff0c;可以用…

hypervisor display显卡节点card0生成过程

ditsi 配置 lagvm/LINUX/android/vendor/qcom/proprietary/devicetree/qcom direwolf-g9ph.dts #include "direwolf-vm-la.dtsi" direwolf-vm-la.dtsi #include "display/quin-vm-display-la.dtsi" quin-vm-display-la.dtsi //对应/sys/class/drm/card…

软件测试面试八股文(超详细整理)

请你说一说测试用例的边界 参考回答&#xff1a; 边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。通常边界值分析法是作为对等价类划分法的补充&#xff0c;这种情况下&#xff0c;其测试用例来自等价类的边界。 常见的边界值 1)对16-bit 的整数而言 32…