NJU PA 2022 :激动人心的计算机系统遨游(一)PA0, PA1
前言
- 本片文章主要用来记录在做PA实验中的所思所想和思考题的答案(不一定对,欢迎讨论),涉及到具体怎么实现则不会放出(请自己RTFS!!!,所有问题的来源就是你没能理解系统的一些细节或者知识点掌握不到位)
- 实验指导书网站:https://nju-projectn.github.io/ics-pa-gitbook/ics2022/0.1.html
- 实验选择的汇编语言:riscv32
- 推荐先去b站学习南京大学的计算机系统基础课,再去学计算机系统基础习题课
由于时间关系我没能学习习题课,将来二周目前再去补(哭)我个人感觉是边学边做会更好一点我就是理论课全部上完再做的,知识点有些都忘记了
PA0 开发环境配置
推荐使用vmare进行开发,如果使用WSL2,需要自己配置图形界面,安装版本:Ubuntu20.04LTS
其实我折腾了一个礼拜的WSL2没折腾出最后才转的WSL2(流汗)
之前在学校的操作系统课学习过Linux的使用,但是用的并不熟练,只知道terminal中的一些命令。什么Makefile,vim更是听都没有听说过老师只会念PPT,但其实我也没好好听罢了
直到做完PA回过头来再看,我才理解到了Makefile和vim的重要性和便利性,还有gdb调式工具,帮助了我修好了一个又一个BUG
- Makefile 能够完成对大型系统中程序的编译,链接和运行,需要学习相关语言
- VIM 为什么是最牛逼的文本编辑器,因为他能让你只用键盘编辑,不用去挪鼠标,而且功能非常丰富
此外TMUX是我做到后面才知道厉害的一个工具,能够将terminal分成许多个窗口,方便之后不同程序内的代码改写和查看
最后的重要一点,贯穿整个PA旅程的三大法则:
- RTFW
- RTFS
- RTFM
(受益匪浅)
PA1 最简单的计算机
PA1实际上就是让我们写一下一个基本的调试器,但是这个调试器我都没怎么用,可别学我(忏悔),能让nemu正确的运行,顺便理解一下基础设施和TRM的相关概念,还有KISS法则
简单介绍一下PA1中的NEMU,它的本质是将计算机硬件部分抽象成软件进行运行,换句话来说,它是一个最简单的图灵机(TRM),能够模拟基本的数字电路元器件,如加法器,寄存器,存储器以及PC(这可不是NEMU的全部,后续的内容还会继续提到)
以下是通过伪代码描述程序的运行流程
while (1) {
从PC指示的存储器位置取出指令;
执行指令;
更新PC;
}
其中传递最重要的概念:程序是一个状态机以及全自动执行
全自动执行不必多说,CPU会不停的重复上述过程,直到程序结束。而状态机则很有意思则,指导书上写的很明白: 给定一个程序, 把它放到计算机的内存中, 就相当于在状态数量为N的状态转移图中指定了一个初始状态, 程序运行的过程就是从这个初始状态开始, 每执行完一条指令, 就会进行一次确定的状态转移. 也就是说, 程序也可以看成一个状态机(指导书中有图,能够更清晰地理解)
这个状态可以具体理解为对于硬件来说就是CPU中各寄存器中的值(通过汇编语言我们可以发现,每条指令都只是更改CPU寄存器内的值),软件中则是各个变量的值(这样说有没有问题?)
(举例来说,就项GDB中调试时使用的单步调试)
必做题1:
格式:(PC,r1,r2) PC为程序计数器,r1,r2分别为两个不同的计数器(0,x , x) -> (1, 0 ,x) -> (2, 0, 0) ->(3, 0, 1) -> (4 , 1 , 0) -> (5, 1 ,2) -> (6, 3, 0) - >(7, 3, 3) -> (8 , 6, 0) ..........->(199, 99801,99) - >(200,99900,0) ->(201 , 99900, 100) -> (202, 10000,0)
RTFSC中的蓝框思考:
宏:
宏用来进行条件编译,来决定预处理中引用或调用哪些库和头文件(?)
函数:
增加代码的可读性: 使用函数可以降低代码的重复性,且能明确表明函数所处理的功能,逻辑清晰
参数:
由控制台调用可执行文件时提供的参数决定,也可能是Makefile文件中提供的参数;
程序正常结束
C语言中,main()函数执行完 return 0后,这个0会返回到exit()函数上并作为它的参数结束程序cpu.exec(int a)
a为程序执行的步数 ,a=-1时为 执行到程序结束为止(2022/7/20 更新:-1在unsigned int 中代表着最大数)
为什么要使用无符号类型?
有符号整数和无符号整数运算时所带来的隐式转换问题(例如-66324*12313),还有就是在除0判断时能够省略正无穷和负无穷
框架代码中定义wp_pool等变量的时候使用了关键字static, static在此处的含义是什么? 为什么要在此处使用它?
static 表示 监测点池 是一个静态的变量。 static变量存放在静态数据段(.robss),可以让监测点池不受函数退出时不会被系统回收
如果你在运行稍大一些的程序(如microbench)的时候使用断点, 你会发现设置断点之后会明显地降低NEMU执行程序的效率. 思考一下这是为什么? 有什么方法解决这个问题吗?
因为使用断点后每次pc变化时都要和每个断点进行比较看是否触发。
解决方案:
读取断点地址的最大值和最小值,比较时若不在次范围内就无需比较
x86的int3指令不带任何操作数, 操作码为1个字节, 因此指令的长度是1个字节. 这是必须的吗?
是的,因为1个字节能够替换掉任意一条指令的第一个字节,可以率先被debugger识别出执行中断而不是执行后面的
假设有一种x86体系结构的变种my-x86, 除了int3指令的长度变成了2个字节之外, 其余指令和x86相同. 在my-x86中, 上述文章中的断点机制还可以正常工作吗? 为什么?
不能,因为在拥有函数的语句中若采用两个字节的int3,该指令会覆盖之后的指令导致错误。
踩坑记录:
1. 写表达式生成器时,生成的可执行文件名expr 和linux指令expr 重名导致报错: missing operand

浙公网安备 33010602011771号