6.s081操作系统Lab4: trap

文章目录

  • 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 TRAPFRAMEcsrrw a0, sscratch, a0# save the user registers in TRAPFRAMEsd ra, 40(a0)sd sp, 48(a0)sd gp, 56(a0).............# save the user a0 in p->trapframe->a0csrr t0, sscratchsd t0, 112(a0)# restore kernel stack pointer from p->trapframe->kernel_spld sp, 8(a0)# make tp hold the current hartid, from p->trapframe->kernel_hartidld tp, 32(a0)# load the address of usertrap(), p->trapframe->kernel_trapld t0, 16(a0)# restore kernel page table from p->trapframe->kernel_satpld t1, 0(a0)csrw satp, t1sfence.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 returnjr 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 callif(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.Sw_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 tablep->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stackp->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 modex |= SSTATUS_SPIE; // enable interrupts in user modew_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, a1sfence.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 TRAPFRAMEld ra, 40(a0)ld sp, 48(a0)ld gp, 56(a0)。。。。# restore user a0, and save TRAPFRAME in sscratchcsrrw 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 againp->calling = 1;//"re-arm" the alarm counter after each time it goes offp->ticks_count = 0;//save and restore registersmemmove(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.rhkb.cn/news/220067.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在jenkins容器中安装python+httprunner+pytest+git+allure(一)

背景&#xff1a; API接口自动化使用python语言实现&#xff0c;利用httprunner框架编写自动化用例场景&#xff08;执行的时候还是依赖pytest),使用jenkins自动构建git上的源代码&#xff0c;并产生allure报告可视化展示API执行结果。 步骤 1.进入jenkins容器 注意使用roo…

【密码学基础】Diffie-Hellman密钥交换协议

DH介绍 Diffie-Hellman密钥协议算法是一种确保共享密钥安全穿越不安全网络的方法。 这个机制的巧妙在于需要安全通信的双方可以用这个方法确定对称密钥&#xff0c;然后可以用这个密钥进行加密和解密。 但是注意&#xff0c;这个密钥交换协议 只能用于密钥的交换&#xff0c;而…

Python PDF转DOCX文档

第三方包&#xff1a;pdf2docx from pdf2docx import Converterdef convert_pdf_to_docx(pdf_path, docx_path):# 创建一个转换器对象converter Converter(pdf_path)# 将PDF转换为DOCXconverter.convert(docx_path, start0, endNone)# 关闭转换器converter.close()# 调用函数…

跨域的解决方式(java后端)

文章目录 一、跨域介绍1、什么是跨域2、为什么会产生跨域3、禁止跨域的原因 二、简单请求和非简单请求1、简单请求1.1、什么时简单请求1.2、简单请求基础流程 2、非简单请求2.1、预检请求2.2、预检请求的回应2.3、浏览器的正常请求和回应 3、自定义跨域过滤器 三、解决方式1、C…

【C++】模板

这篇博客来说一下模板&#xff0c;模板有函数模板和类模板&#xff0c;先来看函数模板&#xff0c;你一听模板这个词就是提前给好一个模具&#xff0c;等我们用的时候在去套用 比如说&#xff1a;我们在实际应用中常常用到swap这个交换函数&#xff0c;但是呢&#xff0c;我们要…

大模型应用_FastGPT

1 功能 整体功能&#xff0c;想解决什么问题 官方说明&#xff1a;FastGPT 是一个基于 LLM 大语言模型的知识库问答系统&#xff0c;提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排&#xff0c;从而实现复杂的问答场景&#xff01;个人体会…

配置Nginx解决跨域问题

Nginx 中将前端请求中的所有以 “/apiUrl” 开头的路径代理到 http://192.12.200.101:9813 例如&#xff1a; /apiUrl/login > http://192.12.200.101:9813/login 配置nginx环境 进入Nginx 的配置文件编辑界面: sudo nano /etc/nginx/conf.d/default.conf开始编辑 defaul…

C# WPF上位机开发(动态添加控件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 写图形界面软件的时候&#xff0c;我们经常会遇到一种情况。那就是图形界面上面&#xff0c;显示的控件可能是不定的。有可能多&#xff0c;也有可…

PyTorch官网demo解读——第一个神经网络(2)

上一篇&#xff1a;PyTorch官网demo解读——第一个神经网络&#xff08;1&#xff09; 继上一篇文章我们展示了第一个神经网络的完整代码&#xff0c;今天我们来聊聊这个神经网络的模型设计。 这个demo实际上只使用了一个简单的线性模型&#xff1a;y wx b&#xff1b; 手写…

软件测试用例经典方法 | 单元测试法案例

单元测试又称模块测试&#xff0c;是对软件设计的最小单元的功能、性能、接口和设计约束等的正确性进行检验&#xff0c;检查程序在语法、格式和逻辑上的错误&#xff0c;并验证程序是否符合规范&#xff0c;以发现单元内部可能存在的各种缺陷。 单元测试的对象是软件设计的最…

微信小程序背景图片设置

问题 :微信小程序通过css:background-image引入背景图片失败 [渲染层网络层错误] pages/wode/wode.wxss 中的本地资源图片无法通过 WXSS 获取&#xff0c;可以使用网络图片&#xff0c;或者 base64&#xff0c;或者使用<image/>标签 解决方法微信小程序在使用backgroun…

HPM6750系列--第十篇 时钟系统

一、目的 上一篇中《HPM6750系列--第九篇 GPIO详解&#xff08;基本操作&#xff09;》我们讲解了HPM6750 GPIO相关内容&#xff0c;在进一步讲解其他外设功能之前我们有必要先讲解一下HPM6750的时钟系统。 时钟可以说是微控制器系统中的心脏&#xff0c;片上外设模块必须依赖时…

如何用Adobe Audition 检测波形的pop和卡顿

在Adobe Audition中&#xff0c;检测卡顿和pop的方法各有不同&#xff1a; 1. **检测卡顿**&#xff1a; - 使用“诊断”面板中的“删除静音”或“标记音频”选项可以帮助识别音频中的静音段落&#xff0c;这可能表明存在卡顿。 - 配置诊断设置&#xff0c;指定静音的振…

Linux中使用podman管理容器

本章主要介绍使用podman管理容器 了解什么是容器&#xff0c;容器和镜像的关系安装和配置podman拉取和删除镜像给镜像打标签导出和导入镜像创建和删除镜像数据卷的使用管理容器的命令使用普通用户管理容器 对于初学者来说&#xff0c;不太容易理解什么是容器&#xff0c;这里…

qt实现基本文件操作

先通过ui界面实现基本框架 接下来就要实现每个按键的功能了 我们先来实现新建的的功能&#xff0c;我们右键新建键&#xff0c;可以发现没有转到槽的功能&#xff0c;因此我们要自己写connect来建立关系。 private slots:void newActionSlot(); 在.h文件中加上槽函数。 conne…

DHTMLX Suite v8.3发布!深化JavaScript UI小部件库使用体验

DHTMLX UI 组件库允许您更快地构建跨平台、跨浏览器 Web 和移动应用程序。它包括一组丰富的即用式 HTML5 组件&#xff0c;这些组件可以轻松组合到单个应用程序界面中。 DHTMLX Suite v8.3已于近日正式发布啦&#xff01;这个更新附带了一组新特性和改进&#xff0c;旨在促进您…

产品入门第五讲:Axure交互和情境

目录 一.Axure交互和情境的介绍 1.交互介绍 概念 常见的Axure交互设计技巧 2.情境介绍 概念 常见的Axure情境设计技巧&#xff1a; 二.实例展示 1.ERP登录页到主页的跳转 2.ERP的菜单跳转到各个页面 &#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个…

2024最新FL Studio21.2MAC电脑版中文版下载安装步骤教程

FL Studio 简称FL&#xff0c;全称Fruity Loops Studio&#xff0c;因此国人习惯叫它"水果"。目前最新版本是FL Studio21.1.1.3750版本&#xff0c;它让你的计算机就像是全功能的录音室&#xff0c;大混音盘&#xff0c;非常先进的制作工具&#xff0c;让你的音乐突破…

基于网络爬虫技术的网络新闻分析

目录 前言 一、网络爬虫技术 二、代理IP 三、网络新闻分析 总结&#xff1a; 前言 随着互联网的发展和普及&#xff0c;网络新闻成为人们获取信息的重要途径。然而&#xff0c;由于网络新闻的数量庞大&#xff0c;分析和处理这些新闻变得愈发困难。本文将介绍如何使用网络…

TestSSLServer4.exe工具使用方法简单介绍(查SSL的加密版本SSL3或是TLS1.2)

一、工具使用方法介绍 工具使用方法参照&#xff1a;http://www.bolet.org/TestSSLServer/ 全篇英文看不懂&#xff0c;翻译了下&#xff0c;能用到的简单介绍如下&#xff1a; 将下载的TestSSLServer4.exe工具放到桌面上&#xff0c;CMD命令行进入到桌面目录&#xff0c;执…