南京大学ICS课程PA实验:从源码到说明书,一套能跑通的计算机系统实践路径

发布时间:2026/10/12 16:52:45

南京大学ICS课程PA实验:从源码到说明书,一套能跑通的计算机系统实践路径 简介这份资源是南京大学ICS课程PA实验部分的完整配套材料面向正在修读计算机系统基础课程或希望深入理解计算机底层原理的学生与自学者。内容围绕NEMU模拟器、抽象机、Makefile构建、nanos-lite轻量级内核及Navy-Apps应用等模块展开帮助读者在源码层面实践指令集、内存管理、中断处理与操作系统接口设计。压缩包共487个文件约741KB以C源码188个与头文件145个为主体辅以Makefile、汇编、C及Kconfig、链接脚本等构建配置另含README说明与初始化脚本结构完整便于按模块检索。目前已有560人学习下载。通过阅读源码与说明书读者可掌握从抽象机到微型操作系统的实现脉络理解编译、链接与系统启动流程并借助示例应用与脚本完成环境配置与调试是理论结合实践、提升系统编程能力的优质课程作业参考。1. 南京大学ICS课程PA实验从源码到说明书一套能跑通的计算机系统实践路径如果你正在搜“南京大学ICS课程PA实验部分-内含源码和说明书.zip”大概率你手里已经拿到了这个压缩包或者正在犹豫要不要花时间啃它。PAProgramming Assignment是南京大学《计算机系统基础》课程的核心实践环节目标是用C语言从零实现一个简化版NEMU模拟器再在其上跑通AM裸机程序和Nanos-lite操作系统。它不像普通课程作业那样填几个函数就完事而是要求你真正理解指令执行、内存管理、中断异常、系统调用这条完整链路。适合谁适合已经学过C语言和数字逻辑、想补上“程序怎么在硬件上跑起来”这一环的本科生或转行工程师。源码和说明书的价值在于你不需要从零猜框架但必须自己填核心逻辑否则调试时连日志都看不懂。2. 先搞清楚PA到底在造什么NEMU、AM与Nanos-lite三层结构2.1 三层各自负责什么别一上来就改错文件PA实验的代码框架通常分为三个目录层级NEMU模拟器、AM抽象机器、Nanos-lite操作系统。NEMU负责取指、译码、执行是硬件行为的软件替身AM提供裸机运行时环境包括TRM运行时环境、IOEIO扩展、CTE上下文扩展等模块Nanos-lite则实现系统调用、文件系统和进程调度。很多新手翻车是因为在NEMU里调系统调用或者在Nanos-lite里改指令译码——这两层之间隔着AM和系统调用接口改错位置会浪费大量时间。常见做法是先让NEMU跑通内置的测试指令再让AM的hello world输出字符最后才碰Nanos-lite。说明书里一般会给出每个阶段的验收命令比如make run后看到HIT GOOD TRAP才算通过。如果你拿到的源码里已经有部分实现先别急着覆盖用git diff或文件对比工具看清楚哪些是框架代码、哪些是待填的TODO。2.2 源码目录的典型布局与阅读顺序拿到压缩包后解压出来的目录结构通常类似这样ics-pa/ ├── nemu/ # 模拟器主体 │ ├── src/ │ │ ├── cpu/ # 指令译码与执行 │ │ ├── memory/# 物理内存与MMIO │ │ └── monitor/ # 调试器与命令解析 │ └── Makefile ├── abstract-machine/ # AM裸机环境 │ ├── am/ │ ├── klib/ # 内核基础库 │ └── scripts/ ├── nanos-lite/ # 操作系统 │ ├── src/ │ └── Makefile └── README.md # 说明书入口阅读顺序建议先看README.md里的实验阶段划分再打开nemu/src/cpu/exec下的指令实现文件对照RISC-V或x86手册看每条指令的语义。说明书通常会列出每个阶段要实现的函数名和预期行为比如exec_once()、isa_exec()、vaddr_read()等。不要跳过Makefile里面藏着编译选项和链接脚本改错一个-march参数就会导致模拟器行为异常。2.3 说明书怎么用才不白读说明书不是小说不要从头读到尾。正确用法是每开始一个阶段先读该阶段的“实验要求”和“验收标准”然后带着问题去翻“实现提示”。比如PA1要求实现指令执行你就重点看exec_once的调用链和decode_exec的译码逻辑。说明书里出现的代码片段通常是伪代码或关键片段不能直接复制需要结合框架里的宏定义和结构体补全。提示说明书里提到的“必做”和“选做”要分清。必做部分不做后续阶段跑不起来选做部分可以最后补不影响主线验收。3. 用源码跑通第一个阶段NEMU指令执行的最小闭环3.1 环境准备与编译命令PA实验对工具链有要求通常需要gcc、make、gdb、readline库。在Ubuntu 20.04或22.04上先装依赖sudo apt update sudo apt install build-essential gdb libreadline-dev libsdl2-dev然后进入nemu目录编译cd ics-pa/nemu make clean make如果编译报错先看是不是缺少libreadline-dev或libsdl2-dev。有些版本的框架会默认开启SDL显示如果不需要图形界面可以在Makefile里把CONFIG_SDL关掉减少依赖。编译成功后运行make run如果看到Welcome to NEMU!和调试器提示符(nemu)说明框架已经跑起来了。但此时执行指令可能会报“未实现”或直接HIT BAD TRAP因为核心译码逻辑还没填。3.2 指令译码与执行的关键代码位置以RISC-V为例nemu/src/isa/riscv32/instr/decode.c里通常有一个decode_exec()函数框架会给出操作码匹配的骨架你需要补全INSTPAT宏里的模式匹配。比如// 示例补全add指令的译码与执行 INSTPAT(0000000 ????? ????? 000 ????? 01100 11, add, R, { // s0 src1, s1 src2, d dest R(d) R(s0) R(s1); });这段代码的逻辑是根据指令的二进制模式提取源寄存器src1、src2和目的寄存器dest然后执行加法并写回。参数说明?????是通配符匹配任意5位寄存器编号0000000和01100是固定的funct7和opcode。如果你把funct3写错比如把000写成001就会导致add被译码成sub程序行为完全错乱。3.3 用内置测试验证指令实现NEMU通常自带一套指令测试集放在nemu/src/isa/riscv32/instr/或nemu/tests/下。运行方式make test或者手动加载镜像(nemu) c如果所有指令测试通过会输出类似All tests passed!的提示。如果某个测试失败调试器会停在出错指令处用info r查看寄存器值用x/10i $pc反汇编当前指令流对比预期结果。常见错误包括符号扩展没做、立即数拼接顺序反了、寄存器写回用了错误的索引。注意不要跳过测试直接跑AM或Nanos-lite。指令层有bug上层所有程序都会以奇怪的方式崩溃到时候排查成本翻倍。4. 从AM到Nanos-lite把裸机程序和操作系统跑起来4.1 AM的TRM与IOE让hello world输出字符AM层的目标是让一个裸机程序能在NEMU上运行。以abstract-machine/am/src/riscv/nemu/为例你需要实现putch()和halt()。putch()通常通过写MMIO地址实现// 示例NEMU的串口MMIO地址 #define SERIAL_PORT 0xa00003f8 void putch(char ch) { outb(SERIAL_PORT, ch); }outb是AM提供的IO端口写函数最终会调用NEMU里实现的mmio_write。参数说明SERIAL_PORT是NEMU约定的串口地址写进去的字节会被NEMU捕获并输出到终端。如果你把地址写错字符不会显示但程序也不会报错——这种“静默失败”是PA里最折磨人的坑之一。验证方式cd abstract-machine/am-kernels/hello make ARCHriscv32-nemu run看到Hello, World!输出即通过。4.2 Nanos-lite的系统调用与文件系统Nanos-lite阶段要求实现syscall分发、fs文件系统和proc进程管理。以sys_write为例// 示例Nanos-lite的sys_write实现 int sys_write(int fd, void *buf, size_t count) { if (fd 1 || fd 2) { for (size_t i 0; i count; i) { putch(((char *)buf)[i]); } return count; } return -1; }逻辑说明标准输出和标准错误直接走putch其他文件描述符需要查文件表。参数fd是文件描述符buf是用户态缓冲区指针count是字节数。注意用户态指针不能直接解引用需要通过copy_from_user或类似机制转换否则在启用分页后会触发页错误。4.3 跑通第一个用户程序从加载到退出Nanos-lite通常内置一个简单的用户程序比如/bin/hello。运行流程cd nanos-lite make ISAriscv32 make run如果看到用户程序输出并正常退出说明系统调用、文件加载、进程切换这条链路基本通了。如果卡住不动先用NEMU的调试器(nemu)按c继续然后CtrlC中断看$pc停在哪里。常见问题加载地址算错、栈指针没设置、sys_exit没实现导致进程无法终止。提示Nanos-lite的调试信息通常通过Log()宏输出可以在Makefile里调整LOG_LEVEL但不要开太多否则串口输出会淹没真正的错误。5. 避坑与排查PA实验里最容易翻车的5个地方5.1 现象NEMU启动后直接HIT BAD TRAP没有任何输出原因通常是复位向量或第一条指令的地址设错或者init_monitor()里没有正确加载镜像。解决检查CONFIG_MBASE和CONFIG_PC_RESET_OFFSET是否与链接脚本一致用x/10i $pc看第一条指令是不是预期的启动代码。5.2 现象指令测试通过但AM的hello world输出乱码原因putch的MMIO地址写错或者NEMU的串口设备没有正确注册。解决确认SERIAL_PORT与NEMU里serial_io_handler的映射地址一致检查outb的端序处理是否正确。5.3 现象Nanos-lite加载用户程序后卡死无任何输出原因用户程序的入口地址或栈指针设置错误或者syscall分发时参数传递错位。解决在do_syscall里打印系统调用号确认a7寄存器的值是否符合预期检查trap上下文保存与恢复是否完整。5.4 现象启用分页后用户程序访问合法地址却触发页错误原因页表项权限位设置错误或者map()时虚拟地址与物理地址的偏移算错。解决用vaddr_read和paddr_read分别读同一地址对比值是否一致检查页表项的V、R、W、X位。5.5 现象编译通过但链接报错“undefined reference to ...”原因框架里某些函数声明了但没实现或者Makefile里的源文件列表没包含你新增的.c文件。解决用grep -r 函数名找到声明位置确认实现文件是否在编译目标里检查SRCS变量是否用了通配符。6. 进阶技巧用差分测试和日志分级把调试效率提上来PA实验做到后期最耗时的不是写代码而是定位“为什么这条指令行为不对”。我自己的习惯是在NEMU里加一个差分测试模式用同一段指令序列分别跑你的实现和参考实现比如QEMU或 Spike逐条对比寄存器状态。具体做法是在exec_once后加一个钩子把pc、gpr[]、csr[]写进环形缓冲区出错时dump最后N条。这个技巧在PA2和PA3阶段能省下大量单步调试的时间。另一个实用习惯是日志分级。NEMU的Log()宏支持DEBUG、INFO、WARN、ERROR等级别我一般把指令译码的详细日志设为DEBUG只在需要时用logctl打开系统调用和异常入口设为INFO默认输出。这样正常运行时不会刷屏出问题时又能快速定位。说明书里通常不会写这些但源码里的utils/log.h已经提供了机制花十分钟改一下Makefile里的CFLAGS就能生效。最后说一个验证方法每通过一个阶段用git tag打一个快照。PA的框架代码和你的实现混在一起一旦改崩了没有版本控制就只能重新解压压缩包。我见过太多人因为没打tag在PA3阶段想回退到PA2的可用状态却回不去最后从头重做。源码和说明书是死的怎么用活取决于你愿不愿意在工程习惯上多花一点心思。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/12 16:47:44

MATLAB编程进阶:从脚本到工程级代码的实战指南

写MATLAB程序这件事,我前后写了小十年。从最早用它做课程作业里的矩阵运算,到后来处理现场采集的传感器数据、搭建仿真模型、甚至给别人做工具包,可以说MATLAB既是入门最容易的工具,也是最能暴露一个人编程习惯的镜子。这个系列走…

2026/10/12 17:47:56

MBD持续集成实践:Simulink模型自动化验证流水线搭建指南

做了几年基于模型的开发(MBD),我早年的工作方式其实挺原始:打开Simulink,手改模型,手动拖测试用例,跑完看波形,再花半小时整理报告。直到有一次,一个改动很小的分支在主干…

2026/10/12 17:47:56

宽带认知雷达波形优化:检测与估计的联合设计方法

简介:联合目标雷达特征估计与目标检测的宽带认知雷达波形优化是一篇面向雷达信号处理与认知雷达领域研究人员、工程师的研究论文。论文针对宽带认知雷达中目标雷达特征(TRS)随目标姿态与时间变化的问题,提出在确保TRS估计精度的前…

2026/10/12 17:47:56

DBmotion全量迁移容器集合:Docker Compose部署实战指南

简介:面向部署数据库迁移工具的开发与运维人员,这份容器镜像集合将迁移引擎、代理服务、监控告警、可视化界面等组件打包为可一键编排的部署方案。包内共十一个文件,包含十个gz格式的容器镜像包和一个YAML编排文件,整体大小约七百…

2026/10/12 17:47:56

HALCON 22.11架构跃迁:从脚本到工业视觉中间件

1. 这不是“第12篇笔记”,而是HALCON工程落地的分水岭时刻很多人看到“HALCON学习笔记 12”这个标题,第一反应是:又一篇按序号堆砌的入门教程?翻到第12篇,大概率还在讲图像采集、阈值分割、blob分析这些基础操作——没…

2026/10/12 17:47:56

兰博基尼按下纯电暂停键:超跑电动化的现实瓶颈与混动过渡

兰博基尼把电动超跑按下暂停键这事儿,在圈子里讨论热度不低。纯电超跑Revuelto的纯电继任者原计划不出意外的话会按时落地,结果集团层面突然调整节奏,把纯电车型时间线往后推了好几年。这个动作背后不是简单的“不玩了”,而是整个…

2026/10/12 17:42:55

西门子S7-200 PLC八层电梯控制系统梯形图逻辑详解

做电梯控制这行当久了,你会慢慢发现一个道理:楼有多高、层有多少其实都是表象,真正决定项目难度的,永远是控制逻辑怎么组织。尤其是八层电梯,不上不下、尴尬得很——比六层多了两个外呼按钮,比十几层的高楼…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/12 12:01:26

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/12 12:54:07

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/12 0:04:22

绝缘子缺陷检测数据集清洗与工业级训练实战指南

简介:本资源是面向电力AI研发人员、工业视觉工程师及智能巡检系统开发者的绝缘子缺陷检测专用YOLO格式数据集,解决无人机航拍场景下绝缘子破损、污闪、积雪等9类典型缺陷的精准识别与定位难题。数据集共2139张真实巡检图像(含训练/验证/测试集…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑