6.s081操作系统Lab4: trap

2023-12-19 02:45
文章标签 操作系统 s081 trap lab4

本文主要是介绍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();}

这篇关于6.s081操作系统Lab4: trap的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/510660

相关文章

Linux操作系统 初识

在认识操作系统之前,我们首先来了解一下计算机的发展: 计算机的发展 世界上第一台计算机名叫埃尼阿克,诞生在1945年2月14日,用于军事用途。 后来因为计算机的优势和潜力巨大,计算机开始飞速发展,并产生了一个当时一直有效的定律:摩尔定律--当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍。 那么相应的,计算机就会变得越来越快,越来越小型化。

1、简述linux操作系统启动流程

1、简述linux操作系统启动流程 启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它。这是因为BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息、PnP特性等等。开机时将ROM中的指令映射到RAM的低地址空间,CPU读取到这些指令,硬件的健康状况进行检查,按照BIOS中设置的启

操作系统是怎么为不同的程序分配所需的内存空间的

操作系统为不同的程序分配内存空间的过程涉及多个关键步骤,确保每个程序都有其所需的内存资源,同时避免程序之间的冲突。以下是操作系统如何为程序分配内存空间的详细过程: 1. 内存管理的基础概念 虚拟内存:现代操作系统使用虚拟内存机制来为程序提供隔离的内存空间。每个程序运行在其独立的虚拟地址空间中,这使得程序间的内存互不干扰。物理内存:实际的 RAM(随机存取存储器),由操作系统和硬件共同管理。虚拟

操作系统安全保护

操作系统安全概述 概念:满足安全策略要求,具有响应安全机制及安全功符合特定安全标准,在一定约束条件下 能抵御常见网络安全威胁,保障自身安全运行及资源安全 安全等级:根据安全功能和安全保障要求分为 用户自主保护级  系统审计保护级 安全标记保护级 结构化保护级 访问验证保护级 操作系统作用: 负责计算系统的资源管理、支撑和控制各种应用程序运行,为用户提供计算机系统管理接口 是构成网络信息

Linux操作系统命令集(一)

最近开了操作系统的课,弄着虚拟机的linux系统命令学学 文件和目录操作命令: ls:列出目录内容 示例:ls -l 以长格式列出目录内容cd:切换目录 示例:cd /home/user 切换到 /home/user 目录mkdir:创建目录 示例:mkdir new_directory 创建名为 new_directory 的目录rmdir:删除空目录touch:创建空文件或更新文件的时间戳

操作系统分页式存储管理

每次输入地址后,计算出页号,若页号越界,则给出错误提示。否则依次调用FIFO和LRU算法,这里值得注意的是,由于我们的FIFO算法先于LRU算法被调用,那么当在处理FIFO算法时,我们暂且不将位视图相应位置做变化,留到处理LRU算法再做处理。 对于FIFO、LRU算法的缺页,我们分两种情况考虑,第一种是模拟栈内还有空间,那么直接将其入栈。第二种是模拟栈内无空间,要发生置换。发生置换时把模拟栈最底

linux定时监听ssh服务是否启动-------麒麟操作系统永久关闭swap

linux监听ssh服务是否启动 1、监听脚本2、定时任务3、麒麟操作系统,永久关闭swap 1、监听脚本 #在/usr/local/bin目录下新建脚本文件 cd /usr/local/bintouch check_sshd.sh#给可执行权限chmod +x /usr/local/bin/check_sshd.sh 脚本内容如下: #!/bin/bashs

【银河麒麟高级服务器操作系统实例】虚拟化平台系统服务中断现象分析及处理建议

服务器环境以及配置 【机型】虚机 处理器: Kunpeng-920 内存: 40G 【内核版本】 4.19.90-23.8.v2101.ky10.aarch64 【OS镜像版本】 银河麒麟操作系统 Kylin-Server-10-SP1-Release-Build20-20210518-arm64 【第三方软件】 智能运维系统、mysql数据集群 现象描述 环境描

VMware17 虚拟机下载以及 CentOS8 操作系统安装配置 一条龙全教程

目录 一、安装 vmware workstation 虚拟机  二、安装 CentOS8 操作系统 三、安装 FinalShell 远程连接 一、安装 vmware workstation 虚拟机     安装中...(耐心等待)  到此安装完成,点击启动运行  激活码如下:  MC60H-DWHD5-H80U9-6V85M-8280D

操作系统之虚拟机

虚拟机(Virtual Machine, VM)是一种通过虚拟化技术将一台物理计算机虚拟化为多个独立的虚拟计算机的技术。每个虚拟机都可以运行自己的操作系统,仿佛它们是独立的物理计算机。虚拟机的引入大大提高了计算资源的利用率,并提供了更大的灵活性。 虚拟机的定义 虚拟机是通过虚拟化技术在一台物理计算机上创建的多个虚拟计算环境。每个虚拟环境(虚拟机)都可以运行一个独立的操作系统和应用程序,仿