PA1: nemu相关学习记录
编译中出错:提示没有 <SDL2/SDL.h>头文件
查看错误信息,出错的文件在abstract-machine文件夹里,叫input.c,里面引用了SDL2/SDL.h 这个头文件,但是我只在fceux文件夹里搜到了sdl.h,考虑到linux区分大小写,搜索以后,我觉得我应该是系统缺少了SDL库。
sudo apt install libsdl2-dev 安装以后编译通过,和fc文件夹的sdl.h没有关系。
然后运行make ARCH=native mainargs=k run,经过检查,按键没有问题。
ysyx项目文件架构:框架 官方文档 (oscc.cc)
nemu/include/generated/autoconf.h 这个文件是一些宏定义相关,nemu/include/config/auto.conf 这个文件是一些配置参数,大致是编译时要选择的编译参数。
make menuconfig打开完整选项
----------------------
插入一下编译c/c++的过程:
1. 预编译,ifdef这样的语句在这一步会被处理,头文件也会被引入。c++的话,constexpr标记的函数也会在这一步处理,生成.i文件
2. 编译 生成.s文件 ,对应即将代码变成汇编文件
3. 汇编 将上一步得到的汇编文件变为二进制文件 对应.o后缀
4. 链接 将.o文件与库链接,生成可执行的.exe文件
想要直接把test.c生成带调试的可执行文件 就需要gcc -g -o test test.c 这条指令
------------------
讲义里给出了makefile的规则:
$(OBJ_DIR)/%.o: %.c @echo + CC $< @mkdir -p $(dir $@) @$(CC) $(CFLAGS) -c -o $@ $< $(call call_fixdep, $(@:.o=.d), $@)
第一行表示 obj_dir 这个变量对应的目录下,每个.o文件都依赖于同名.c文件
第二行表示命令行输出+CC 和第一个依赖文件的名字 $<表示第一个依赖文件
第三行表示创建目标文件的所在目录 $@表示目标文件 dir $@就是目标文件所在目录
第四行是规则,表示gcc 以CFLAGS参数编译第一个依赖文件,生成目标文件
这三行前面有@,表示这三行不显示出来
最后一行则是调用自定义函数,同时传入两个参数,一个是.o文件对应的.d文件,一个是目标文件
终端会显示的内容:(也就是第四行指令对应内容)
gcc -O2 -MMD -Wall -Werror -I/home/user/ics2023/nemu/include -I/home/user/ics2023/nemu/src/engine/interpreter -I/home/use r/ics2023/nemu/src/isa/riscv32/include -O2 -D__GUEST_ISA__ =riscv32 -c -o /home/user/ics2023/nemu/build/obj-riscv32-nem u-interpreter/src/utils/timer.o src/utils/timer.c
$@ -> /home/user/ics2023/nemu/build/obj-riscv32-nemu-interpreter/src/utils/timer.o
$< -> src/utils/timer.c 剩下的都是CFLAGS
-o表示要指定生成的文件的名字 它和后面紧挨的参数是一起的
----------------------------------
init_monitor这个函数里调用别的函数而不是直接把代码展开,可以让函数看起来更简洁,此外被调用的函数在其他地方也可以进行二次利用。
此外,对于nemu/src/monitor/monitor.c这个文件,针对不同架构,通过宏决定了哪些需要进行编译。
在nemu目录下make run以后,报错信息同讲义,提示monitor.c:36:welcome:assertion '0' failed。同时make指令会提示scripts/native.mk:38:run 会提示core dumped
检查montitor.c,发现这一行是assert(0) 断言这么用似乎没什么意义,所以我屏蔽了。
疑问回答:cpu_exec()这个函数的参数是无符号整数,传入-1,实际上是传入int型最大值,大致相当于while(1)。
--------------------------------以下一段是错误的,留作记录,不要学习-----------
关于程序的进入和退出。在进入时,系统会将pc寄存器的值设置为当前程序的首条指令地址。退出时,系统会检测退出码,0就是向系统回报正常退出的信号。然后系统释放掉这个进程,如果有父进程的话,向父进程发一个SIGCHLD信号量,通知子进程结束的消息。
假如make run以后,直接在nemu中输入q,那么native.mk:38会提示报错。打开这个mk文件,第38行对应NEMU_EXEC这个变量,这个变量对应 $(BINARY) $(ARGS) $(IMG)
直接退出报错,可能是因为当前还在执行make指令,但是直接退出,对应的sdb.c里是返回-1值。返回非0值,那么make报错。 我把这一行变量后面加了个 ||true,这样最终这行返回一定是零值。加上以后,确实不再报错。
makefile的工作原理(个人理解):make指令运行到程序开始运行,并不代表make已经运行结束。nemu里直接退出,导致程序在退出时没有正确返回值,所以make的这一行指令也没有对应到零值,进而报错。对于make,返回零才表示是正常退出
如果想要不报错,要么用 ||true,要么在这一行指令前加 - ,表示忽略错误继续执行。
---------------24/7/1-------------正确的理解----------
上面的记录是错误的,如果在makefile中添加代码,那相当于在执行完成后掩盖错误信息,是掩耳盗铃的行为。要实现优雅的退出,应该有的思路是:在非正确退出时,把所有退出前应该干的活都处理掉。
从nemu的main函数入手,我们可以看到engine_start()和is_exit_status_bad()两个函数。前者是主函数,后者则是用来检查nemu的退出状态。进入engine_start(),我们可以看到sdb_mainloop()函数,这又跳转到了我们已经不断完善的sdb.c。这样一看来,程序流程就很显而易见了:nemu启动后,会顺序进入sdb的mainloop函数,然后无限循环读取指令,直到强制退出或者q退出。
退出时,进入is_exit_status_bad()函数,这个函数判断nemu的退出状态是否符合原目标。
那么,怎么实现“优雅的退出”呢?怎么样才算优雅呢?至少在makefile里修改肯定不是,因为这样做只是修改了程序退出的状态信息。内层并没有得到妥善处理。 修改退出状态函数is_exit_status_bad()也是同理。 想要优雅退出,在主循环里就应该对退出做出反应。