惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
D
Docker
S
SegmentFault 最新的问题
博客园 - 聂微东
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
GbyAI
GbyAI
L
LangChain Blog
Vercel News
Vercel News
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
Engineering at Meta
Engineering at Meta
T
Threat Research - Cisco Blogs
T
Threatpost
Scott Helme
Scott Helme
T
Tailwind CSS Blog
Latest news
Latest news
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
The Register - Security
The Register - Security
罗磊的独立博客
P
Proofpoint News Feed
腾讯CDC
S
Schneier on Security
雷峰网
雷峰网
A
About on SuperTechFans
T
Tenable Blog
F
Full Disclosure
Cyberwarzone
Cyberwarzone
博客园_首页
有赞技术团队
有赞技术团队
K
Kaspersky official blog

文章列表

游戏玩后感:ReLief:献给亲爱的你 我的周边(谷子)分享 游戏玩后感:Kanon 简谱:致真实的你 《Rust中常见的有关生命周期的误解》学习笔记 简谱:StarMap 简谱:かく咲きたらばいと恋ひめやも 简谱:东风 简谱:无法诉说的思念 简谱:Girlish 游戏玩后感:时钟机关的Layline 简谱:风之琶音 简谱:星空的记忆 简谱:因为遇见了你 简谱:月童 番茄简谱脚本转调器 游戏玩后感:青空下的约定:Refine 游戏玩后感:在这苍穹展翅 书籍读后感:控制论与科学方法论 游戏玩后感:恋爱表达式 游戏玩后感:樱之诗 MLIR-tutorial学习笔记 游戏玩后感:潜伏之赤途 游戏玩后感:纯爱咖啡厅:帕露菲重制版 游戏玩后感:智以泪聚 游戏玩后感:初雪樱 游戏玩后感:告别回忆:从今以后 游戏玩后感:梦灯花 游戏玩后感:金辉恋曲四重奏 游戏玩后感:五彩斑斓的世界 昇腾310P使用记录 游戏玩后感:AIR 游戏玩后感:弹丸论破 游戏玩后感:流景之海的艾佩莉亚 Xilinx_HLS上板过程记录 游戏玩后感:告别回忆2 游戏玩后感:恋爱绮谭 Faiss和Rapidsai_Raft使用记录 游戏玩后感:近月少女的礼仪 游戏玩后感:樱色之云,绯色之恋 游戏玩后感:幸运草的约定 游戏玩后感:星之梦、候鸟和丸子与银河龙 游戏玩后感:白色相簿2 Windows上使用VTune分析PyTorchExtension调用的Cpp程序 SpinalHDL上板过程记录 游戏玩后感:仰望夜空的星辰 最简单的算卦方法之一:梅花易数法 游戏玩后感:苍之彼方的四重奏 krkr引擎解包工具介绍 自定义CUDA实现PyTorch算子的四种简单方法 游戏玩后感:星空的记忆 游戏玩后感:9nine 游戏玩后感:AtriMyDearMoments 游戏玩后感:极限脱出 游戏玩后感:魔女的夜宴 SSH实现多跳代理 动漫观后感:向山进发 flv重封装H264、AAC流 动漫观后感:夏日重现 CSP模板 游戏玩后感:海沙风云 动漫观后感:灵能百分百 游戏玩后感:交响乐之雨 游戏玩后感:爱上火车LastRun 游戏玩后感:LittleBustersEX 游戏玩后感:SummerPockets 游戏玩后感:逆转裁判 Ultra96V2开发板简单使用 SpinalWorkshop实验笔记(三) SpinalWorkshop实验笔记(二) SpinalWorkshop实验笔记(一) PYNQ开发板上使用USB声卡+OSS兼容层播放音频 TestOS移植K210开发板 rCore-Tutorial-Book-v3学习笔记(七) 动漫观后感:凉宫春日的忧郁 rCore-Tutorial-Book-v3学习笔记(♭七) rCore-Tutorial-Book-v3学习笔记(六) rCore-Tutorial-Book-v3学习笔记(五) rCore-Tutorial-Book-v3学习笔记(四) rCore-Tutorial-Book-v3学习笔记(三) rCore-Tutorial-Book-v3学习笔记(二) rCore-Tutorial-Book-v3学习笔记(一) 游戏玩后感:RewritePlus MIT-6.S081-2020实验(xv6-riscv64)十一:net MIT-6.S081-2020实验(xv6-riscv64)十:mmap MIT-6.S081-2020实验(xv6-riscv64)九:fs MIT-6.S081-2020实验(xv6-riscv64)八:lock MIT-6.S081-2020实验(xv6-riscv64)七:thread MIT-6.S081-2020实验(xv6-riscv64)六:cow MIT-6.S081-2020实验(xv6-riscv64)五:lazy MIT-6.S081-2020实验(xv6-riscv64)三:pgtbl MIT-6.S081-2020实验(xv6-riscv64)二:syscall 动漫观后感:吹响吧上低音号 MIT-6.S081-2020实验(xv6-riscv64)一:util 快速生成网络mp4视频缩略图技术 用plantuml画图示例 QQ缩略图和大图不同实现 Python制作字符图片 动漫观后感:命运石之门 Unity3D+Post_Processing_Stack_V2自定义后处理效果研究
MIT-6.S081-2020实验(xv6-riscv64)四:traps
VnYzm · 2021-01-01 · via

实验文档

概述

这次实验内容比较分散,总体来说难度不是太高。

内容

Backtrace

要求在内核中对程序的调用栈进行遍历,输出每一级调用的返回地址。首先我们分析Riscv的栈帧结构,查看call.asm中main函数的汇编代码:

void main(void) {
  1c:   1141                    addi    sp,sp,-16
  1e:   e406                    sd  ra,8(sp)
  20:   e022                    sd  s0,0(sp)
  22:   0800                    addi    s0,sp,16
  printf("%d %d\n", f(8)+1, 13);
  24:   4635                    li  a2,13
  26:   45b1                    li  a1,12
  28:   00000517            auipc   a0,0x0
  2c:   7c050513            addi    a0,a0,1984 # 7e8 <malloc+0xea>
  30:   00000097            auipc   ra,0x0
  34:   610080e7            jalr    1552(ra) # 640 <printf>
  exit(0);
  38:   4501                    li  a0,0
  3a:   00000097            auipc   ra,0x0
  3e:   27e080e7            jalr    638(ra) # 2b8 <exit>

栈是由高地址向低地址增长的,可见在进入函数时,会由高向低依次压入ra寄存器的值(当前函数的返回地址)、s0寄存器的值(功能上类似x86的ebp寄存器),然后令s0寄存器指向当前函数的栈帧首部。之后再有什么局部变量sp寄存器再向下。可见我们只需要逐级取出栈中存储的s0寄存器的值,就可以定位到每一级的栈帧头部,就能获得栈中存储的ra寄存器的值了。什么时候结束呢,注意到每个进程的用户栈只有一页,因此对栈中任意位置调用PGROUNDUP宏就可以获得栈底向上一个字节的位置,所以如果只要当前得到的s0寄存器大于等于刚才得到的位置就可以终止了:

void backtrace(void) {
    uint64 fp = r_fp(), base = PGROUNDUP(fp);
    printf("backtrace:\n");
    while (fp < base) {
        printf("%p\n", *((uint64*)(fp - 8)));
        fp = *((uint64*)(fp - 16));
    }
}

Alarm

要求在时钟中断的处理程序中调用用户态的函数并返回,主要涉及的是两个态的切换,不过因为相关的汇编代码程序中已经有了,调用函数就行了。这个和我做8086汇编实验时编写的时钟中断处理程序相比有更难的地方,也有更容易的地方。难主要是这个包含不同态的切换。容易主要在这个中断处理函数可以直接由操作系统来调用,时钟中断触发的函数是内核里的代码,由内核负责统一调度需要执行什么函数;而8086则需要自己手动修改中断矢量表,让其指向自己的中断处理函数,同时还需要自己进行一些段寄存器的切换等内存管理操作,稍有不慎就系统崩溃,挺麻烦的。

回到这个实验,添加系统调用函数的过程就不说了,和实验二相似,sys_sigalarm函数内部主要是把一些信息保存到proc结构体里,重点是usertrap函数里时钟中断相关的内容,当判断当前已注册时钟中断处理函数且到达调用的时刻数时,由于时钟中断处理函数在用户态,所以顺着函数后面的usertrapret就能回到用户态,但是必须保证回到用户态时执行的是处理函数的函数头,观察上面系统调用相关的代码注意到回到用户态时执行的代码位置是由p->trapframe->epc决定的,所以修改这个属性就可以了。

这是test0的部分,test1和test2的部分要求在处理函数调用sys_sigreturn后要能够回到时钟中断前的程序运行位置,而且回到那个位置时寄存器什么的状态都和以前完全一样。观察到usertrap函数中proc结构体里的trapframe就是干这个活的,存储中断前的状态,所以我就直接在proc结构体里新声明了另一个trapframe结构体,然后进入处理函数前把trapframe属性的值复制给新trapframe保存,调用sys_sigreturn时就把新trapframe的值送回给原trapframe属性,就能恢复原来的状态了:

  if(which_dev == 2) {
      if (p->ticks > 0 && p->duration > -1) {
          p->duration++;
          if (p->duration >= p->ticks) {
              p->duration = -1;
              p->state_time = *p->trapframe;
              p->trapframe->epc = p->handler;
              intr_on();
          } else yield();
      } else yield();
  }

p->ticks表示执行处理函数的间隔,p->duration表示距离上一次执行处理函数的时刻数。这里我用p->duration=-1表示当前正在执行处理函数。p->state_time就是新添加的trapframe结构体。yield函数的作用是让出CPU(标记当前进程为RUNNABLE然后调度),有点像异步程序中执行IO时的挂起,个人感觉在执行处理函数的时候就接着运行别让出CPU了,不过网上也看到有人一律yield的,这个应该无所谓,因为在用户看来系统怎样调度进程和自己没啥关系,只要程序能正常运行就行了。

uint64 sys_sigalarm(void) {
    if(argint(0, &myproc()->ticks) < 0)
        return -1;
    if(argaddr(1, &myproc()->handler) < 0)
        return -1;
    return 0;
}

uint64 sys_sigreturn(void) {
    struct proc *p = myproc();
    p->duration = 0;
    *p->trapframe = p->state_time;
    return 0;
}