6.s081 学习实验记录(五)traps
文章目录
- 一、RISC-V assembly
- 简介
- 问题
- 二、Backtrace
- 简介
- 注意
- 实验代码
- 实验结果
- 三、Alarm
- 简介
- 注意
- 实验代码
- 实验结果
一、RISC-V assembly
简介
- git checkout traps,切换到traps分支
user/call.c文件在我们输入make fs.img之后会被汇编为call.asm文件,阅读该汇编文件中的函数:f、h、main- call.c 中的代码比较简单:
#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);
}
问题
我们需要回答以下问题:
- 哪些寄存器用于传参? 例如,
main函数调用printf时持有13 这个参数

RISC-V 寄存器作用如下图:
通过main函数的汇编代码,可以知道参数 13 使用a2寄存器传递

main函数对应的汇编代码在哪里调用了函数f ? 函数 g 的调用在哪里?(编译器可能会优化某些函数为内联函数)
由问题一查看的汇编可知,main函数并未调用函数f,而是直接由编译器优化得到了结果printf函数的地址是什么?
printf的地址为0x64a(虚拟地址),main函数中的调用也能说明这一点


main函数中使用jalr到printf之后,ra寄存器的值是什么?
jalr指令执行时,会将pc + 4的值存放到ra寄存器中作为返回地址,因此ra的值为0x38- 运行下面的代码,输出是什么?(输出取决于大小端,RISC-V为小端模式,如果是大端,相同输出需要将 57616 改为多少?)
- unsigned int i = 0x00646c72;
- printf(“H%x Wo%s”, 57616, &i);
- 输出为:
He110 World - 因为,57616对应16进制的 0xe110,0x00646c72 以小端存储,实际为 0x6c720064,
0x6c对应r,0x72对应l,0x64对应d(00代表NULL,不显示)。如果是大端,则高地址在高处,因此应该存储为0x6c720064或者0x6c7264
- 下面的代码,
y=后面的输出是什么?为什么?- printf(“x=%d y=%d”, 3);
- 输出为:乱码,因为参数传递时使用的寄存器是确定的,因此如果第二个参数不传递,依然还是会用
a2寄存器传递,此时a2中的值是caller中的残留值
二、Backtrace
简介
当发生错误时,打印当前的调用栈。每个函数栈帧包含一个返回地址以及一个指向caller栈帧的 frame pointer(我理解就是caller的rsp)
在kernel/printf.c 中实现一个 backtrace(),在 sys_sleep 中插入对该函数的调用,然后运行 bttest,该用户程序会调用 sleep 系统调用,此时应该打印堆栈
bttest 在qemu中执行完毕之后,新开一个窗口执行 addr2line -e kernel/kernel (or riscv64-unknown-elf-addr2line -e kernel/kernel),将有如下输出:

阅读:kernel/sysproc.c:74、kernel/syscall.c:224、kernel/trap.c:85
注意
kernel/defs.h中添加backtrace()函数的声明- gcc产生汇编时,会将栈指针存放在
s0寄存器,在kernel/riscv.h中添加如下代码:
static inline uint64
r_fp()
{uint64 x;asm volatile("mv %0, s0" : "=r" (x) ); //含义为读取 s0 寄存器中的值,赋值给 x 并返回 xreturn x;
}
返回地址位于栈指针 - 8的位置,保存的栈指(caller的栈指针地址)针位于当前函数的栈指针的-16处(栈指针即rbp)- 你需要识别最后一个栈帧并结束,由于函数栈最大为4KB,因此可以使用
PGROUNDDOWN(fp) (see kernel/riscv.h)来判断是否到达最后一个函数栈
实验代码
思路:
1、首先 kernel/riscv.h 添加要求的代码,kernel/defs.h 声明 backtrace()
2、 backtrace() 的逻辑为:使用 r_fq() 获取当前栈指针,打印栈指针 - 8 的地址(即调用栈),并 -16,得到上一个栈帧的地址,依次类推,直到地址等于 PGROUNDDOWN(fp) (see kernel/riscv.h)
kernel/riscv.h
static inline uint64
r_fp()
{uint64 x;asm volatile("mv %0, s0" : "=r" (x) ); //含义为读取 s0 寄存器中的值,赋值给 x 并返回 xreturn x;
}
kernel/defs.h
void backtrace(void);
kernel/printf.c
void backtrace(void){// 获取当前栈帧uint64 addr = r_fp();// 向上遍历while(addr != PGROUNDDOWN(addr)){printf("%p\n", *(uint64 *)(addr - 8));addr = *(uint64 *)(addr - 16);}
}
kernel/sysproc.c
uint64
sys_sleep(void)
{int n;uint ticks0;argint(0, &n);//...其他代码backtrace();return 0;
}
实验结果


三、Alarm
简介
添加一项新的功能,定时报警,即简单的用户级中断/故障处理程序。为以后缺页中断打下基础。
添加一个新的系统调用 sigalarm(interval, handler),如果用户态应用调用 sigalarm(n, fn),那么每当该程序消耗 n 个cpu ticks,内核会调用一次 fn 。当调用 sigalarm(0, 0) 时停止。
注意
- makefile 中添加
user/alarmtest.c - 正确的结果输出如下:

实验代码
思路:proc中添加三个字段,分别记录用户设置的ticks,cur_ticks以及callback,系统调用则是设置进程的 ticks 以及callback,在处理时钟中断时,给当前的进程cur_ticks 加1,判断和ticks是否相等,相等则调用callback。
时钟中断的处理函数为 clockintr(),当用户态进程运行时来了时钟中断,调用路径为:usertrap---判断类型---devintr
详细流程:参考 xv6-book 第四章
trap发生时, cpu 硬件会做一些准备 (book:44页),该步骤设置了SIE位, 保存 pc,设置trap原因,进入内核态(但是不切换内核页表以及内核栈),执行 trap 处理函数(stvec寄存器保存的地址)
如果是在用户态发生 trap,此时处理函数为 uservec,该函数保存了用户寄存器以及其他状态,切换内核页表(所以trap发生时,硬件只保证切换到内核态,即cpu设置为管理模式,但是并不会切换页表,切换页表是在trap处理函数中做的),最后跳转到 usertrap()执行。
因此,我们需要在 usertrap 函数中添加对于时钟中断的判断逻辑以及用户callback的调用逻辑(需要保证不可重入,即我们如果在执行 callback 的时候再次被时钟中断trap,此时不应该再次执行callback,所以还需要在proc.h中添加一个字段用于防止重入)。usertrap中本身有对于时钟中断的判断逻辑,即进程调度的部分,具体代码如图(所以照猫画虎即可):

最后还有一点需要注意,我们的callback函数是应该在用户态运行的(因为内核态无法解析用户态的虚拟地址,所以无法调用callback),此时我们处于内核态,因此我们需要保存上下文,切换到用户态,执行完callback之后,切换回内核态,恢复上下文,继续usertrap的执行。
如何切换到内核态呢?此时我们借助 usertrapret() 来返回用户态,所以我们需要更改trap的返回地址,当前 trap 的返回地址为 p->trapframe->epc += 4;(kernel/trap.c:61)。所以,我们首先保存一下当前 trapframe,然后更改 trapframe->epc = callback,这样在 usertrapret() 返回用户态后执行的就是 callback 函数,此时需要注意,callback函数是不能有参数的(因为这不是正常的函数调用)
callback执行成功了,但是我们该如何返回到用户态代码原先的地方执行呢? 因此,实验测试代码中提供了另外一个系统调用 sigreturn(),来让我们重新回到内核态,恢复进入 trap 时的用户上下文,在这一次的 usertrapret() 返回到用户进程原先执行的地方。(xv6 设计的中断、系统调用走相同的调用路径,因此这里可以执行这样的骚操作。)
总体流程:
用户态程序用户态程序—>时钟中断-->usertrap() (p->trapframe->epc 保存用户态程序的下一条执行语句)—>保存trapframe 到另外一个地方(假设为A)—> 更改p->trapframe = callback—>usertrapret() 返回用户态执行callback—>通过sigreturn() 重新进入trap流程—>此时走系统调用路径---->sys_sigreturn() 恢复 p->trapframe 为最开始保存的(从A处读取)—>usertrapret() 返回用户态trap中断处继续执行- 一次
alarm trap需要往返内核态两次
代码:
- kernel/proc.h
// Per-process state
struct proc {struct spinlock lock;//新增字段int ticks; //回调执行所需要ticksint cur_ticks; //当前进程的ticksvoid* callback; //回调函数struct trapframe backup; //保存trapframeint execing; //是否已经在执行了
- makefile
UPROGS=\$U/_alarmtest\
- user/user.h
int sigalarm(int ticks, void (*callback)());
int sigreturn(void);
- user/usys.pl
entry("sigalarm");
entry("sigreturn");
- kernel/syscall.h
#define SYS_sigalarm 22
#define SYS_sigreturn 23
- kernel/syscall.c
extern uint64 sys_mkdir(void);
extern uint64 sys_close(void);
//新增
extern uint64 sys_sigalarm(void);
extern uint64 sys_sigreturn(void);static uint64 (*syscalls[])(void) = {
//其他
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
// 新增
[SYS_sigalarm] sys_sigalarm,
[SYS_sigreturn] sys_sigreturn,
};
- kernel/sysproc.c
uint64
sys_sigalarm(void){int ticks = 0;uint64 callback = 0;argint(0,&ticks);argaddr(1, &callback);struct proc* proc = myproc();proc->ticks = ticks;proc->callback = (void *)callback;proc->execing = 0;return 0;
}uint64
sys_sigreturn(void){struct proc* proc = myproc();*(proc->trapframe) = proc->backup;proc->execing = 0;// 由于该函数返回后,返回值保存在a0中,会覆盖trapframe中原来的a0// 该函数返回之后就会走 usertrapret() 返回到用户态return proc->trapframe->a0;
}
- kernel/trap.c
if(killed(p))exit(-1);// 新增代码 if(which_dev == 2){struct proc* proc = myproc();// 设置了ticks 且没有执行if(proc->ticks > 0 && proc->execing == 0){proc->cur_ticks++;if(proc->cur_ticks == proc->ticks){proc->execing = 1;proc->backup = *(proc->trapframe); //保存trapframeproc->trapframe->epc = (uint64)proc->callback;proc->cur_ticks = 0;}}}// 新增结束// give up the CPU if this is a timer interrupt.if(which_dev == 2)yield();
实验结果
- make qemu

- ./grade-lab-traps running alarmtest

相关文章:
6.s081 学习实验记录(五)traps
文章目录 一、RISC-V assembly简介问题 二、Backtrace简介注意实验代码实验结果 三、Alarm简介注意实验代码实验结果 一、RISC-V assembly 简介 git checkout traps,切换到traps分支user/call.c 文件在我们输入 make fs.img 之后会被汇编为 call.asm 文件…...
探索设计模式的魅力:从单一继承到组合模式-软件设计的演变与未来
设计模式专栏:http://t.csdnimg.cn/nolNS 在面对层次结构和树状数据结构的软件设计任务时,我们如何优雅地处理单个对象与组合对象的一致性问题?组合模式(Composite Pattern)为此提供了一种简洁高效的解决方案。通过本…...
文心一言4.0API接入指南
概述 文心一言是百度打造出来的人工智能大语言模型,具备跨模态、跨语言的深度语义理解与生成能力,文心一言有五大能力,文学创作、商业文案创作、数理逻辑推算、中文理解、多模态生成,其在搜索问答、内容创作生成、智能办公等众多…...
Python循环语句——while循环的嵌套应用
一、引言 在Python编程中,循环结构是必不可少的。其中,while循环因其灵活性而备受青睐。嵌套while循环,即将一个while循环放置在另一个while循环内部,能让我们编写更为复杂的程序逻辑。本文将详细介绍嵌套while循环的基本概念、使…...
数据库管理-第145期 最强Oracle监控EMCC深入使用-02(20240205)
数据库管理145期 2024-02-05 数据库管理-第145期 最强Oracle监控EMCC深入使用-02(20240205)1 监控方式2 度量配置3 阻塞4 DG监控总结 数据库管理-第145期 最强Oracle监控EMCC深入使用-02(20240205) 作者:胖头鱼的鱼缸&…...
Centos 7系统安装proftpd-1.3.8过程
一、下载安装: 1、网站中能够下载到profptd源码: http://www.proftpd.org/ 这个是软件源码ftp地址: ftp://ftp.proftpd.org/distrib/source/ 2、进入目录/root/download解压: tar -zxvf proftpd-1.3.8.tar.gz #将源码压缩包解压…...
DevExpress ASP.NET Web Forms v23.2最新版本系统环境配置要求
本文档包含有关安装和使用 DevExpress ASP.NET Web Forms控件的系统要求的信息。 点击获取DevExpress v23.2正式版(Q技术交流:909157416) .NET Framework DevExpress ASP.NET Web Forms控件和MVC扩展支持以下.NET Framework 版本。 如果您需要 DevExp…...
5分钟快速掌握 XML (Extensible Markup Language)
背景 在Java开发的过程中,我们经常需要和配置文件打交道,其中接触最多的就是XML。从最初学习 JavaWeb 时在 Tomcat 中配置servlet,到后来接触Spring框架并在XML中编写各种配置,XML一直是不可或缺的一部分。然而,XML的…...
Python中的HTTP代理服务器和客户端的区别与联系
在Python编程中,当我们涉及到网络通信,尤其是HTTP请求时,经常会听到“HTTP代理服务器”和“客户端”这两个词。它们在网络世界中扮演着不同的角色,但又有着紧密的联系。 区别 首先,我们来谈谈它们的区别。 HTTP代理…...
升级Oracle 单实例数据库19.3到19.22
需求 我的Oracle Database Vagrant Box初始版本为19.3,需要升级到最新的RU,当前为19.22。 以下操作时间为为2024年2月5日。 补丁下载 补丁下载文档参见MOS文档:Primary Note for Database Proactive Patch Program (Doc ID 888.1)。 补丁…...
在Vue中如何动态绑定class和style属性
在Vue中,动态绑定class和style属性是我们经常遇到的需求。这个功能允许我们根据不同的条件来动态改变元素的样式,让我们的应用更加灵活和富有交互性。在本篇博客文章中,我将带你深入探索在Vue中如何实现这一功能。 首先,让我们了…...
使用Docker部署DashDot服务器仪表盘并结合cpolar实现公网监测服务器
最近,我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念,而且内容风趣幽默。我觉得它对大家可能会有所帮助,所以我在此分享。点击这里跳转到网站。 文章目录 1. 本地环境检查1.1 安装docker1.2 下载Dashdot镜像 2.…...
Android kernel logcat时间戳显示错乱修改
前言 在抓取log时发现,kernel log合logcat的时间戳不对应。在分析log时,造成很大的困扰 修改 针对Android 10 的修改 diff --git a/system/core/logd/LogKlog.cpp b/system/core/logd/LogKlog.cpp old mode 100644 new mode 100755 index edd326a..f…...
2024年考PMP还有什么用?
PMP 是项目管理专业人士资格认证的意思,也是项目管理领域通用的证书, 做项目的基本都会去考。 要说 PMP 有啥作用? 个人感觉 PMP 证书更多的是跳槽、转行的敲门砖的作用,因为现在很多公司都要 PMP 证书,有了可以加分…...
解决zabbix图像中文乱码
使用zabbix查看监控图像信息,发现会有中文乱码现象。 解决方法如下: 1.拷贝windows文字文件到服务器上 C:\Windows\Fonts目录下拷贝自己需要的中文语言文件 2.修改配置文件 vim /usr/share/zabbix/include/defines.inc.php 81行 define(ZBX_GRAPH_F…...
centos间文件传输
scp /home/vagrant/minio zx192.168.56.34:/home/zx /home/vagrant/minio 是你要传输的文件而且是当前机器登录用户有权限操作的文件 zx是目标机器的用户192.168.56.34是目标机器的地址 /home/zx是要传到这个文件夹下 要确保zx有/home/zx这个文件夹的操作权限 本质就是ssh文…...
2.0 Zookeeper 安装配置
Linux 安装 zookeeper 下载地址为: Apache ZooKeeper。 选择一稳定版本,本教程使用的 release 版本为3.4.14,下载并安装。 打开网址 https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz,看到如下界…...
Matomo 访问图形显示异常
近期我们的把 PHP 系统完全升级后,访问 Matomo 的站点有关访问的曲线无法显示。 出现的情况如下图: 我们可以看到图片中有关的访问曲线无法显示。 如果具体直接访问链接的话,会有下面的错误信息。 问题和解决 出现上面问题的原因是缺少 ph…...
MySQL学习记录——사 表结构的操作
文章目录 1、创建表2、查看表结构3、改变表结构4、删除表5、总结 1、创建表 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; 例子 create table users ( id int, name varchar(20) c…...
【华为 ICT HCIA eNSP 习题汇总】——题目集12
1、企业网络内部常常采用私有 IP 地址进行通信,以下哪个地址属于私有 IP 地址? A、0.1.1.1 B、127.5.4.3 C、128.0.0.5 D、172.24.35.36 考点:网络层 解析:(D) A类 IP 地址中,10.0.0.0 ~ 10.255…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
