xv6源码分析 013

2024-04-30 06:44
文章标签 分析 源码 013 xv6

本文主要是介绍xv6源码分析 013,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

xv6源码分析 013

从这一章开始我们就开始学习xv6内核的陷入机制(trap)。其中主要的函数是usertrapkerneltrap,这两个函数的实现有点像。

我们需要了解用用户态陷入内核态并从内核态返回用户态,从内核态进行陷入并返回的整个过程,以及会调用那些函数,陷入之前的进程的上下文如何被保存,保存在哪里,陷入过程中需要改变那些寄存器的值,从陷入中恢复之后需要如何恢复这些寄存器的值,等等。

xv6_book/chapter4.md · Prim./mit6.S086 - 码云 - 开源中国 (gitee.com)

全局变量

struct spinlock tickslock;
uint ticks;extern char trampoline[], uservec[], userret[];// in kernelvec.S, calls kerneltrap().
void kernelvec();

struct spinlock tickslockuint ticks是用于时钟中断的

extern char trampoline[], uservec[], userret[]是汇编函数

void kernelvec()也是一个汇编函数

初始化函数

void
trapinit(void)
{initlock(&tickslock, "time");
}

从用户态陷入

我先用一个图来表示从用户态陷入内核,再从内核返回用户态的整个过程。

在这里插入图片描述

现在我们来看看具体的函数

陷入函数

陷入过程由两部分组成,trampoline page中存放的跳转代码(在trampoline.S中),和usertrap(void)

我们先来看看trampoline.S中陷入内核的部分

uservec:	# 这是一个全局标识,在文件外部也能够使用这个标识# swap a0 and sscratch# so that a0 is TRAPFRAMEcsrrw a0, sscratch, a0  # 这条汇编指令下面会讲解# save the user registers in TRAPFRAME# 将当前cpu中寄存器的状态保存到tramframe中sd ra, 40(a0)sd sp, 48(a0)sd gp, 56(a0)sd tp, 64(a0)sd t0, 72(a0)sd t1, 80(a0)sd t2, 88(a0)sd s0, 96(a0)sd s1, 104(a0)sd a1, 120(a0)sd a2, 128(a0)sd a3, 136(a0)sd a4, 144(a0)sd a5, 152(a0)sd a6, 160(a0)sd a7, 168(a0)sd s2, 176(a0)sd s3, 184(a0)sd s4, 192(a0)sd s5, 200(a0)sd s6, 208(a0)sd s7, 216(a0)sd s8, 224(a0)sd s9, 232(a0)sd s10, 240(a0)sd s11, 248(a0)sd t3, 256(a0)sd t4, 264(a0)sd t5, 272(a0)sd t6, 280(a0)# save the user a0 in p->trapframe->a0# 获取本次陷入内核的缘由csrr t0, sscratchsd t0, 112(a0)# restore kernel stack pointer from p->trapframe->kernel_sp# 从trapframe中加载内核栈的栈指针到sp寄存器ld 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)# satp寄存器:# 用于管理内存地址转换和页表相关的设置,# 比如建立和更改虚拟地址到物理地址的映射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 returnjr t0

我们先来解释一下csrrw a0, sscratch, a0这句汇编。

  1. csrrw表示“读-改-写”(Read-Set-Write)操作,它回显读取一个系统控制寄存器的值,然后用给定的寄存器更新该寄存器,并返回寄存器原始的值。我们需要注意的是:这是一条原子指令,基本用途是:在软件中管理和控制处理器的行为和状态,比如中断功能,计时器或调试功能等。

  2. 参数:

    csrrw rd, csr, rs1
    
    • rd:目标寄存器,用于存放从CSR中读取的原始值
    • csr:要访问的控制状态寄存器的编码
    • rs1:源寄存器,其内容将被写入CSR中。
  3. 指令执行的过程:

    • 读取csr当前的值,并将该值写入到寄存器rd中。
    • 将寄存器rs1中的值写入csr中,完成修改操作

所以这条指令的作用就是原子地交换了两个寄存器的值。我们也可以去了解一条cas指令xchg,这个也是一个原子指令,用在互斥锁中的。

ok,当执行完上面这段trampoline汇编之后,我们就跳转到了内核中,并处于usertrap()函数的入口处,我们现在来执行这个函数,这个函数的主要的功能是路由(routine):判断陷入的原因,并执行相应的处理程序。

usertrap(void)
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.// 跳过ecall指令执行下一条指令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();// 陷入机制的函数一般都是一去不复返的。
}

我们看两点,可能会有疑惑的地方:

  • w_stvec((uint64)kernelvec);

    这行代码的作用是设置系统陷入处理的入口地址(interrupt service routine)。在RISC-V架构中,stvec寄存器存储着中断或异常向量表的基地址。这意味着后续的中断和异常不在由usertrap()来处理,而是交给了kernelvec指向的汇编函数来处理,这是内核在响应这些是将时从用户态转到内核态的一个关键步骤。

  • p->trapframe->epc += 4;

    这行代码理解起来就相对简单,我们知道,调用系统调用进而陷入内核是通过ecall指令,所以ecall是陷入的开始(对内核来说),也是上一次上下文切换的最后一条指令(对用户态进程来说)。在内核中,ecall相当于一条没用的指令,所以我们需要跳过这条指令,指向下一条需要执行的指令,注意这两条指令不一定是在连续的物理内存中的。

明天在讲解剩下的内容。

通过ecall指令,所以ecall是陷入的开始(对内核来说),也是上一次上下文切换的最后一条指令(对用户态进程来说)。在内核中,ecall相当于一条没用的指令,所以我们需要跳过这条指令,指向下一条需要执行的指令,注意这两条指令不一定是在连续的物理内存中的。

明天在讲解剩下的内容。

这篇关于xv6源码分析 013的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。