5.Lab four —— Trap

news2024/9/20 17:30:01

首先切换traps分支

git checkout traps
make clean

RISC-V assembly

代码:

#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int g(int x) {
  return x+3;
}

int f(int x) {
  return g(x);
}

void main(void) {
  printf("%d %d\n", f(8)+1, 13);
  exit(0);
}

汇编后指令:

0000000000000000 <g>:
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int g(int x) {
   0:	1141                	addi	sp,sp,-16    # 栈顶指针下移16字节
   2:	e422                	sd	s0,8(sp)    # 存栈底指针s0/fp到sp+8的位置
   4:	0800                	addi	s0,sp,16    # 将sp+16即原sp的值作为新的栈帧
  return x+3;
}
   6:	250d                	addiw	a0,a0,3    # a0=a0+3, a0即为传入参数x又为返回值
   8:	6422                	ld	s0,8(sp)    # 从sp+8恢复原栈帧到s0
   a:	0141                	addi	sp,sp,16    # 回收栈顶指针
   c:	8082                	ret    # 返回

000000000000000e <f>:    # 与g()函数系统,相当于将g()内联了

int f(int x) {
   e:	1141                	addi	sp,sp,-16
  10:	e422                	sd	s0,8(sp)
  12:	0800                	addi	s0,sp,16
  return g(x);
}
  14:	250d                	addiw	a0,a0,3
  16:	6422                	ld	s0,8(sp)
  18:	0141                	addi	sp,sp,16
  1a:	8082                	ret

000000000000001c <main>:

void main(void) {
  1c:	1141                	addi	sp,sp,-16    # 栈顶指针下移16字节
  1e:	e406                	sd	ra,8(sp)    # 存返回地址到sp+8的位置
  20:	e022                	sd	s0,0(sp)    # 存栈底指针s0/fp到sp的位置
  22:	0800                	addi	s0,sp,16    # 更新栈帧s0
  printf("%d %d\n", f(8)+1, 13);
  24:	4635                	li	a2,13    # 加载13到a2寄存器
  26:	45b1                	li	a1,12
  28:	00000517          	auipc	a0,0x0    # 将pc+0加载到a0
  2c:	7b050513          	addi	a0,a0,1968 # 7d8 <malloc+0xea>
  30:	00000097          	auipc	ra,0x0    # 将pc+0<<12加载到ra寄存器
  34:	600080e7          	jalr	1536(ra) # 630 <printf>    # 将pc设置为ra+1536,并将pc+4写入ra(即进行函数跳转)
  exit(0);
  38:	4501                	li	a0,0
  3a:	00000097          	auipc	ra,0x0
  3e:	27e080e7          	jalr	638(ra) # 2b8 <exit>

1.哪些寄存器保存函数的参数?例如,在main对printf的调用中,哪个寄存器保存13?

答:a0-a7,a2,a0保存格式化字符串,a1保存f(8)+1,a2保存13

2.main的汇编代码中对函数f的调用在哪里?对g的调用在哪里(提示:编译器可能会将函数内联)

答:进行了内敛优化,没有调用的代码,g(x)被内联到f(x)中,f(x)又被内联到main中

3.printf函数位于哪个地址?

答:jalr 1536(ra),其中1536 = 0x600,ra为0x30,所以printf函数地址位于0x630

auipc指令格式:auipc rd,imm , 将当前PC的高20位与立即数imm相加,然后将结构存储在目标寄存器rd中

在这里,前面的数字即代表pc的值可以发现此时是30,即0x30所以printf函数地址位于0x630

4.在main中printf的jalr之后的寄存器ra中有什么值?

答:jalr 指令的下一条汇编指令的地址。将pc设置为ra+1536,并将pc+4写入ra(即进行函数跳转)

5.运行以下代码。 unsigned int i = 0x00646c72; printf("H%x Wo%s", 57616, &i);

5.1 程序的输出是什么?这是将字节映射到字符的ASCII码表。

答:输出为"He110 World",57616 = 0xE110,0x00646c72若是小端存储为72-6c-64-00,对应ASCLL码表为72:r, 6c:l, 64:d, 00:充当字符串结尾标识

5.2 输出取决于RISC-V小端存储的事实。如果RISC-V是大端存储,为了得到相同的输出,你会把i设置成什么?是否需要将57616更改为其他值?

答:i在内存中分布应该仍旧是72-6c-64-00,不过由于是大端存储,所以要改成0x726c6400,而57616不需要更改,因为是通过二进制数字读取的而非单个字符

6.在下面的代码中,“y=”之后将打印什么(注:答案不是一个特定的值)?为什么会发生这种情况? printf("x=%d y=%d", 3);

答;输出的是一个受调用前的代码影响的“随机”的值。因为 printf 尝试读的参数数量比提供的参数数量多。 第二个参数 3 通过 a1 传递,而第三个参数对应的寄存器 a2 在调用前不会被设置为任何具体的值,而是会 包含调用发生前的任何已经在里面的值。 即原本需要两个参数,却只传入了一个,因此y=后面打印的结果取决于之前a2中保存的数据

Bacektrace

在kernel/printf.c中实现名为backtrace()的函数。在sys_sleep中插入一个对此函数的调用,然后运行bttest,它将会调用sys_sleep。你的输出应该如下所示,同时退出后执行addr2line -e kernel/kernel并且把刚才输出的三行粘贴进去可以得到看到执行的对应文件和行数

1.将下列函数添加到kernel/riscv.h中用于获取正在执行的函数的帧指针并保存到s0寄存器,该函数用内联汇编来获取s0

2.在kernel/defs.h中添加backtrace的原型

3.在kernel/printf.c中实现该函数

4.在kernel/sysproc的sys_sleep中插入对该函数的调用

我们可以看看user/bttest里面实现了什么

其实就是运行了一下sleep,然后就会到trap -》syscall -》 sysproc 里面执行到sys_sleep,最后由于事先在sys_sleep里面进行了埋点,那么就会执行backtrace(),打印出这几个调用关系

测试

一些疑问

按照栈的调用关系,sysproc 、syscall、systrap的关系应该如下图所示

最开始的fp的应该是backtrace函数里面的栈帧指针,打印fp-8指向的地址就是sysproc里面sys_sleep里面调用的它的返回地址,然后fp-16解引用拿到的是前一个栈帧指针也就是fp1,打印fp1-8指向的地址就是syscall里面调用的返回地址,然后fp1-16解引用拿到的就是前一个栈帧指针也就是fp2,打印fp2-8指向的地址就是systrap里面调用的返回地址,然后fp2-16解引用拿到的就是无效的指针,就会跳出循环

Alarm

1.在user/user.h中添加两个系统调用的原型函数

在user/usys.pl脚本中添加两个系统调用相应的entry,然后在kernel/syscall.h和kernel/syscall.c中添加相应声明

2.在kernel/proc.h中的struct proc结构体添加记录时间间隔调用函数地址,以及经过时钟数的字段

3.在sysproc.c中编写sys_sigalarm()函数,将interval和handler值存到当前进程的proc结构体中

4.在kernel/proc.c中的allocproc()函数中将上述三个新增字段初始化赋值

5.每次经过设置好的时钟间隔,会引发时钟中断,调用kernel/trap.c里面的usertrap(),对于时钟中断which_dev变量的值为2,由此便可以单独对时钟中断进行操作

// lab4-3
  if(which_dev == 2){   // timer interrupt
    // increase the passed ticks
    if(p->interval != 0 && ++p->passedticks == p->interval){
      p->passedticks = 0;
      p->trapframe->epc = p->handler;   // execute handler() when return to user space 
    }
  }

6.修改makefile文件的UPROGS部分,添加alarmtest.c的编译

7.sigalarm(interval, handler)sigreturn() 两个函数是配合使用的, 在 handler 函数返回前会调用 sigreturn()

前面的做法是调用定时函数实际上修改trapframe->epc进而在返回到用户空间时调用定时函数,但这也产生了问题,原本的ec被覆盖,无法回到中断前用户代码执行的位置,因此需要考虑在 sigalarm() 函数中将寄存器值进行保存, 在 sigreturn() 函数中进行恢复. 这样在执行完 sigreturn() 后程序能够回到原来的执行位置.

在系统调用时用户代码中断时会将寄存器记录到 p->trapframe 中, 而前者由于在 usertrap() 覆盖了 p->trapframe->epc, 才能够执行定时函数, 执行完后又会导致一些寄存器的值被修改. 因此, 考虑在 struct proc 中保存一个 trapframe 的副本, 在覆盖 epc 之前先保存副本, 然后在 sys_sigreturn() 中将副本还原到 p->trapframe 中, 从而在 sigreturn 系统调用结束后恢复用户寄存器状态时能够将执行定时函数前的寄存器状态进行恢复.

8.在kernel/trap.c中的usertrap中覆盖p->trapfram->epc前先把trapframe副本存好

9.编写sysproc.c中的sys_sigreturn

10.一些理解:

开始得到kernel/proc.c中的allocproc()初始化p->trapframecopy为0,一开始没有副本。

同时需要注意的是myproc()->passedticks = 0的时机从原来的usertrap函数改成了sys_sigreturn。因为在usertrap重置后可能会导致重入(例如清零后后面重新计数再次进入handler而此时handler还没执行完)

而放在sys_sigreturn() 之后, 即在最后函数返回前才会清零,此时handler已经执行完成,则不会导致重入。

这个图是sygalarm的处理流程,handler在user/alarmtest.c中最后调用了sigreturn(),对应的就是下图中右边部分,若是到了触发条件回到用户态执行handler但是还得通过sigreturn()进入到内核态把trapframe复制回来

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

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

相关文章

AI预测体彩排3采取888=3策略+和值012路或胆码测试8月24日升级新模型预测第61弹

经过近60多期的测试&#xff0c;当然有很多彩友也一直在观察我每天发的预测结果&#xff0c;得到了一个非常有价值的信息&#xff0c;那就是9码定位的命中率非常高&#xff0c;已到达90%的命中率&#xff0c;这给喜欢打私菜的朋友提供了极高价值的预测结果~当然了&#xff0c;大…

Linux——驱动——自动设备

自动创建设备节点是Linux设备驱动开发中的一个重要环节&#xff0c;它允许设备驱动程序在内核中注册后&#xff0c;自动在/dev目录下创建对应的设备文件&#xff0c;从而使得用户空间程序可以通过标准的文件操作接口&#xff08;如open、read、write等&#xff09;与硬件设备进…

Level3 — PART 4 机器学习算法 — 朴素贝叶斯

目录 贝叶斯定理 朴素贝叶斯模型&#xff08;Naive Bayes Model&#xff09; 估计 离散估计 极大似然估计 案例 朴素贝叶斯扩展 高斯贝叶斯分类器 原理 应用 源码分析 伯努利贝叶斯分类器 原理 源码分析 多项朴素贝叶斯分类器 半朴素贝叶斯分类器 模拟题 CDA…

Linux系统之jobs命令的基本使用

Linux系统之jobs命令的基本使用 一、jobs命令介绍二、jobs命令的使用帮助2.1 jobs命令的help帮助信息2.2 jobs命令的语法解释 三、jobs命令的基本使用3.1 运行一个后台任务3.2 列出后台所有的作业3.3 列出进程ID3.4 只列出进程ID3.5 终止后台任务3.6 只显示运行任务3.7 只显示停…

tcp 网络通信及抓包工具的使用

tcp网络通信 本地回环&#xff08;Loopback&#xff09;的概念 本地回环地址是一个特殊的IP地址&#xff0c;用于指向计算机本身的网络接口。在IPv4中&#xff0c;最常见的本地回环地址是127.0.0.1&#xff0c;而在IPv6中则是::1。这个地址用于测试网络软件&#xff0c;确保网…

【IoT】路由器/linux系统,如何使用shell查看系统硬件配置,传感器CPU温度,资源占用率等信息(以红米AX6000为例)

【IoT】路由器/linux系统&#xff0c;如何使用shell查看硬件配置&#xff0c;传感器CPU温度&#xff0c;系统资源占用率等信息&#xff08;以红米AX6000为例&#xff09; 文章目录 1、路由器拆机与测评&#xff08;Redmi AX6000&#xff09;2、通过telnet获得SSH3、linux系统信…

SpringBoot集成kafka接收消息

SpringBoot集成kafka接收消息 1、SpringBoot集成kafka接收消息2、Payload注解接收消息体内容3、Header注解接收消息头内容4、接收消息所有内容 1、SpringBoot集成kafka接收消息 生产者 package com.power.producer;import org.springframework.kafka.core.KafkaTemplate; imp…

【自动化】考试答题自动化完成答案,如何实现100%正确呢

一、科目仿真考试不能自动答题 我的答案是可以的&#xff0c;电脑程序可以模拟人的操作完成所有的答题并提交结束考试 二、分析页面内容 完成一个题目&#xff0c;包括判断题&#xff0c;对与错2选1答案&#xff0c;单选题ABCD4选1答案&#xff0c;多选题大家想一想 F12查看按…

基于机器学习的糖尿病数据分析与风险评估系统

B站视频及代码下载&#xff1a;基于机器学习的糖尿病数据分析与风险评估系统_哔哩哔哩_bilibili 1. 项目简介 糖尿病&#xff0c;作为一种在全球范围内广泛流行的慢性疾病&#xff0c;已经影响了数以百万计的人们的生活&#xff0c;给全球公共健康带来了严重的挑战。因此&#…

uni-app的示例项目--简单的登陆页面及列表页面

uni-app的示例项目--简单的登陆页面及列表页面 文章说明核心代码效果展示源码下载 文章说明 随着移动端使用占比升高&#xff0c;手机端的App、小程序也成了一些场景下的首选&#xff1b;采用uni-pp开发此类应用具有很多优势&#xff0c;它可以直接使用vue3进行开发&#xff0c…

集合论与位运算之间的转换

集合可以用二进制表示&#xff0c;二进制从低到高第 i 位为 1 表示 i 在集合中&#xff0c;为 0 表示 i 不在集合中。例如集合 {0,2,3} 可以用二进制数 1101(2)​ 表示&#xff1b;反过来&#xff0c;二进制数 1101(2)​ 就对应着集合 {0,2,3}。 例如集合 {0,2,3} 可以压缩成 …

干货|软件测试简历的编写以及注意事项

一、个人信息 1.年龄超过30岁的&#xff0c;就不体现年龄&#xff1b; 2.学历是本科的&#xff0c;以及专业是计算机的可以加上学历、专业2个标签&#xff0c;大专的则可以不体现&#xff1b; 3.英语过了四六级的可以加1个英语的标签&#xff1b; 4.如果你的户籍和面试城市…

Python入门级 序列全集 [ 继上篇 进阶版 持续更新中哞哞哞!!! ]例题较多

本文主要结合例题介绍了序列【常用函数、可迭代对象】&#xff0c;字典【函数、写法、定义、视图对象】&#xff0c;集合【常用函数】&#xff0c;运算符优先级。这几种数据集合在Python中也是蛮重要的&#xff0c;对于新手比较友好。 本文例题大多来自哔站up主鱼C-小甲鱼【Pyt…

系统编程 网络 http协议

http协议------应用层的协议 万维网&#xff1a;http解决万维网之间互联互通 计算机web端网络只能看到文字 1.如何在万维网中表示一个资源&#xff1f; url <协议>&#xff1a;//<主机>&#xff1a;<端口>/<路径> ------------------------------…

Adobe After Effects的插件--------CC Ball Action

CC Ball Action是粒子效果器,其将2D图层变为一个个由3D小球构成的图层。它是AE内置的3D插件。 使用条件 使用该插件的图层需是2D图层。 我们以一张图片素材为例: 给图片图层添加CC Ball Action效果控件,然后新建一个摄像机(利用摄像机旋转、平移、推拉工具,方便在各个角…

【LeetCode面试150】——36有效的数独

博客昵称&#xff1a;沈小农学编程 作者简介&#xff1a;一名在读硕士&#xff0c;定期更新相关算法面试题&#xff0c;欢迎关注小弟&#xff01; PS&#xff1a;哈喽&#xff01;各位CSDN的uu们&#xff0c;我是你的小弟沈小农&#xff0c;希望我的文章能帮助到你。欢迎大家在…

微服务基础与Spring Cloud框架

一、系统架构的演变 1.1单体应⽤架构 Web应⽤程序发展的早期&#xff0c;⼤部分web⼯程(包含前端⻚⾯,web层代码,service层代码,dao层代码)是将 所 有的功能模块,打包到⼀起并放在⼀个web容器中运⾏。 1.2 垂直应⽤架构 当访问量逐渐增⼤&#xff0c;单⼀应⽤增加机器带来的…

【Unity3D小技巧】Unity3D中实现FPS数值显示功能实现

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群&#xff1a;398291828 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 很简单也很使用的小技巧&#xff0c;就是在Unity…

PHP酒店宾馆民宿预订系统小程序源码

酒店宾馆民宿预订系统&#xff1a;一键解锁完美旅行住宿新体验 &#x1f31f; 开篇&#xff1a;告别繁琐&#xff0c;拥抱便捷预订新时代 在这个快节奏的时代&#xff0c;每一次旅行的规划都希望能尽可能高效与省心。想象一下&#xff0c;在规划一场说走就走的旅行时&#xf…

Nature | 小麦D基因组的起源和演化,野生近缘种对作物抗病改良具有重要潜力

image-20240815151428804 2024年8月14日沙特阿卜杜拉国王科技大学Brande B. H. Wulff 和 Simon G. Krattinger团队在Natue发表Origin and evolution of the bread wheat D genome研究论文&#xff0c;通过研究粗山羊草&#xff08;Aegilops tauschii&#xff0c;也被称为节节麦…