Skip to content

Latest commit

 

History

History
193 lines (122 loc) · 15.1 KB

File metadata and controls

193 lines (122 loc) · 15.1 KB

Project Pregame(指南中文译本)

本文为 CS 162 Project Pregame(proj-pregame)官方指南的中文译本。
原文:https://cs162.org/static/proj/proj-pregame/
归档参考:https://web.archive.org/web/20251215163710/https://cs162.org/static/

本仓库说明(自学版)

  • 代码:各 project 共用仓库根目录下的一份 ../src/,本目录只有指南。
  • 文档译本../pintos-docs/
  • 构建 / 测试本作业(在仓库根目录下):
cd src/userprog
make
make check
# 或:pintos-test do-nothing

下文路径已按本仓库改写(官方原文里的 proj-pregame/src/...~/code/personal/... 均对应这里的 src/...)。书面题原要求交 Gradescope;自学可自行记录答案。


Introduction

来源:https://cs162.org/static/proj/proj-pregame/

CS 162 的各个 project 将使用 Pintos——一个教学用操作系统。它们旨在让你在开发真实、可运行的 kernel 的情境中,获得对操作系统核心思想的实践经验,同时又不至于过于复杂。Pintos 的 skeleton code 在其 file system、thread scheduler 以及对 user programs 的支持上存在若干局限。在这些 project 的过程中,你将在上述各个方面大幅改进 Pintos。

本仓库已包含 Pintos 源码(src/),无需再从 staff repository 拉取。

本作业的具体内容见下方 Tasks。不过,你可能会发现先通读部分 Pintos documentation 中文译本 会有帮助。这通常有助于你理解所需完成的任务。

我们要求通读 pintos-docs 的以下章节:

在本次练习中,你将进一步了解 Pintos 的一些基础内容(例如 debugging),并有机会修复 Pintos 中的一个 bug。与后续 project 不同,本次为个人作业,所有工作(包括 code 与书面回答)必须由你独立完成。 本 project 旨在让你感受 Pintos 的结构,并学会使用一些通用的 debugging 工具。


Tasks

来源:https://cs162.org/static/proj/proj-pregame/docs/tasks/

为了给后续 Pintos project 设计出好的解决方案,你首先需要对现有 codebase 有充分理解。本次练习的目标,是帮助你开始熟悉 Pintos 的代码。

目录


Reading the documentation

来源:https://cs162.org/static/proj/proj-pregame/docs/tasks/documentation/

我们要求通读 pintos-docs 的以下章节:

官方课程会在后续的 design documents 与 oral exams 中就这些文档章节提问。自学时也建议能回答诸如 “What address is the top of a user programs stack?” 这类问题。


Find the faulting instruction

来源:https://cs162.org/static/proj/proj-pregame/docs/tasks/faulting_instruction/

首先,在 src/userprog 目录下运行 makemake check,并观察当前没有任何测试通过。

在本次作业中,我们将在 GDB 中逐步跟踪 do-nothing 测试的执行,以学习如何修改 Pintos 使该测试通过,并理解 Pintos 现有对 user programs 的支持是如何实现的。

do-nothing 是检验 Pintos user program 支持的最简单测试。请查看 src/tests/userprog/do-nothing.c;它是一个什么都不做的 Pintos user application。main 函数中唯一的 return 162 语句会向操作系统返回 exit code 162。exit code 的具体数值对本测试并不重要;我们选择了一个非 0 的值,以便更容易通过 GDB 跟踪 Pintos kernel 如何处理该值(注意 162 = 0xa2)。

当你运行 make 时,do-nothing.c 被编译成可执行程序 do-nothing,你可以在 src/userprog/build/tests/userprog/do-nothing 找到它。do-nothing 测试只不过是在 Pintos 中运行该 do-nothing 可执行文件。

查看文件 src/userprog/build/tests/userprog/do-nothing.result。(或者,你也可以在 src/userprog 下运行 pintos-test do-nothing。)该文件展示了 Pintos testing framework 在运行 do-nothing 测试时的输出。testing framework 期望 Pintos 输出 do-nothing: exit(162)。这是 Pintos 在 process 退出时打印的标准消息(你将在 Project Userprog 中再次遇到它)。然而,如 diff 所示,Pintos 并未输出这条消息;相反,do-nothing 程序因 memory access violation(segmentation fault)在 userspace 中崩溃了。

Hint: 如果查找某样东西时卡住了,试试 findgrep!例如,若要按文件名查找文件,可从 src 运行 find -name <file_name>;若要在任意文件中搜索字符串匹配,可从 src 运行 grep -r <string> .

现在我们将引导你调试该测试。根据 do-nothing 测试的输出,请回答下列问题(自学可记在自己的笔记里;课上原交 Gradescope):

  1. 该程序从 userspace 试图访问哪个 virtual address,从而导致崩溃?为什么此时不允许程序访问该 memory address?(请具体说明,并提到 Pintos codebase 中的特定 macros。)
  2. 导致崩溃的那条 instruction 的 virtual address 是什么?
  3. 为了调查,使用 i386-objdump 反汇编 do-nothing binary。程序崩溃时所在的 function 名称是什么?将该 function 的反汇编代码抄下来,并指出程序崩溃时所在的那条 instruction。
  4. 找到你在上面识别出的那个 function 的 C 代码(Hint: 它在 userspace 中执行,因此要么在 do-nothing.c 中,要么在 src/libsrc/lib/user 的某个文件中),并将其抄下来。
  5. 对于 #3 中反汇编 function 里的每条 instruction,试着用几句话向自己说明它为何必要和/或它在试图做什么。Hint: 阅读 80x86 calling convention(见 02-userprog.md)。你在 #3 中识别出的那条 instruction,为什么会试图访问你在 #1 中识别出的那个 virtual address 处的 memory?请给出高层解释,而不要仅仅提及 register 的值。

如果你已经做到这一步,干得好!你现在对测试在何处失败以及为何失败已有较好的认识。这将让我们能更高效地使用 GDB。


Step through the crash

来源:https://cs162.org/static/proj/proj-pregame/docs/tasks/step_through/

既然我们已经理解了 do-nothing 程序为何崩溃,我们将使用 GDB,从 kernel 启动开始,逐步跟踪 Pintos 中 do-nothing 测试的执行。我们的目标是弄清如何修改 Pintos 的 user program loader,使 do-nothing 不再崩溃,同时熟悉 Pintos 如何支持 user programs。为此,将工作目录切换到 src/userprog/ 并运行

FORCE_SIMULATOR=--bochs PINTOS_DEBUG=1 pintos-test do-nothing

补充说明:你可以通过将其加入 bashrc,为其设置别名 pintos-debug

echo "alias pintos-debug='FORCE_SIMULATOR=--bochs PINTOS_DEBUG=1 pintos-test'" >> ~/.bashrc

此时 GDB 应当已经打开。在高层上,Pintos 要启动 do-nothing process,必须先发生以下事情:

  • BIOS 从磁盘的第一个 sector 读取 Pintos bootloader(src/threads/loader.S),并将其载入地址 0x7c00 处的 memory。
  • bootloader 从磁盘读取 kernel code 到地址 0x20000 处的 memory,然后跳转到 kernel entrypoint(src/threads/start.S)。
  • kernel entrypoint 处的代码切换到 32-bit protected mode,然后调用 mainsrc/threads/init.c)。
  • main 函数通过初始化 scheduler、memory subsystem、interrupt vector、hardware devices 以及 file system 来启动 Pintos。

欢迎你阅读代码以进一步了解这一启动过程,但对于 Pintos projects 而言,你并不需要理解其工作原理。

run_task 处设置 breakpoint,并在 GDB 中 continue,以跳过上述 setup。正如你在 run_task 的代码中所见,Pintos 通过调用

process_wait(process_execute("do-nothing"));

来执行(在 Pintos command line 上指定的)do-nothing 程序,该调用位于 run_task 中。process_waitprocess_execute 都在 src/userprog/process.c 中。

现在,回答下列问题:

  1. Step into process_execute 函数。正在运行该函数的 thread 的名称与地址是什么?此时 Pintos 中还存在哪些其他 threads?复制它们的 struct thread。(Hint: 对于最后一部分,dumplist &all_list thread allelem 可能会有用。)
  2. 当前 thread 的 backtrace 是什么?将 GDB 中的 backtrace 复制为你的答案。
  3. start_process 处设置 breakpoint 并 continue 到该处。正在运行该函数的 thread 的名称与地址是什么?此时 Pintos 中还存在哪些其他 threads?复制它们的 struct thread
  4. 逐步跟踪 start_process 函数,直到你已经 stepped over 对 load 的调用。注意 load 会设置 if_ structure 中的 eipesp 字段。打印出 if_ structure 的值,并以十六进制显示(hint: print/x if_)。
  5. asm volatile 语句中的第一条 instruction 将 stack pointer 设置为 if_ structure 的底部。第二条跳转到 intr_exit。代码中的注释解释了这里发生的事情。Step into 该 asm volatile 语句,然后逐步跟踪这些 instructions。当你 step through iret instruction 时,观察该函数“返回”到了 userspace。为什么 processor 在执行该函数时会切换模式?你可以从执行 iret 时 memory 和/或 registers 中的值,以及 iret instruction 的功能来解释。
  6. 执行完 iret 后,输入 info registers 以打印 registers 的内容。将此命令的输出抄下来。这些值与你打印 if_ 时的值相比如何?
  7. 注意,若你试图用 backtrace 获取当前位置,将只会得到一个十六进制地址。这是因为 debugger 只加载了来自 kernel 的 symbols。现在我们已进入 userspace,必须加载我们正在运行的那个 Pintos 可执行文件——即 do-nothing——的 symbols。为此,使用 loadusersymbols tests/userprog/do-nothing。现在,使用 backtrace,你会看到当前位于 _start 函数中。使用 disassemblestepi 命令,在 userspace 中逐条 instruction 执行,直到发生 page fault。此时,processor 已立即进入 kernel mode 以处理 page fault,因此 backtrace 将显示 kernel mode 下的当前 stack,而不是 page fault 发生时的 user stack。不过,你可以使用 btpagefault 来找到 page fault 发生时的 user stack。将 btpagefault 的输出抄下来。

Debug

来源:https://cs162.org/static/proj/proj-pregame/docs/tasks/debug/

你在 GDB 中观察到的 faulting instruction 应当与你在 #3 中找到的相一致。既然你已经确定了 faulting instruction、理解了该 instruction 的用途,并走通了 kernel 如何初始化一个 user process,你现在可以修改 kernel,使 do-nothing 能够正确运行。

  • (Coding test 1) 修改 Pintos kernel,使 do-nothing 不再崩溃。你的改动应当在 Pintos kernel 中,而不是在 userspace 程序(do-nothing.c)或 src/lib 中的 libraries。这不应涉及对 Pintos 源代码的大幅修改。官方 staff solution 仅对 process.c 做了一行改动就解决了该问题。完成此改动后,do-nothing 测试应当通过,但其他测试很可能会失败。Note: 即便你的改动看起来像 hack 也没关系。你将在 user programs project 中实现更好的修复。
  • (Coding test 2) 既然你已理解如何修复 do-nothing,请查看 do-not-much 测试。该测试与 do-nothing 的不同之处在于:它返回 argc,而不是简单地返回 162。目前你可以假定 do-not-much 不接收 command-line arguments。(这与 argc 有何关系?)实现一处能让 do-not-much 通过的改动。同样,该解决方案可以是 hack。Hint: 回顾你在 Find the faulting instruction 第 5 题中的回答。
  • (Coding test 3) 你的修复有可能对 stack-align-v 测试也有效,但对 do-nothingdo-not-much 也存在并不适用于该测试的解决方案。请查看 stack-align-v 测试。它的行为与 do-not-much 类似,但应返回 esp % 16 的值。写下该程序应当返回什么(Hint: 可在 src/tests/userprog/stack-align-v.ck 中找到),以及为何如此。
  • 如有必要,修改你的修复,使 do-nothingdo-not-muchstack-align-v 三者全部通过。请记下你通过 stack-align-v 的解决方案,并明确引用 calling convention 的各个步骤以及相应的 offset 值来证明其正确性。可用 pintos-test <name> 逐个验证。
  • 像之前一样,在 do-nothing 上重新运行 GDB。执行 loadusersymbols 命令,在 _start 处设置 breakpoint,然后 continue,以直接跳到 userspace 执行的开始。使用 disassemblestepi 命令,逐条 instruction 执行 do-nothing 程序,直到到达 src/lib/user/syscall.c 中的 int $0x30 instruction。此时,通过检查 memory 打印 stack 顶部的两个 words(Hint: x/2xw $esp),并复制输出。
  • int $0x30 instruction 会切换到 kernel mode,并在该 process 的 kernel stack 上压入一个 interrupt stack frame。继续逐条 instruction 跟踪,直到到达 syscall_handlerargs[0]args[1] 的值分别是什么,它们与你对上一题的回答有何关系?

现在,你可以继续逐步跟踪 Pintos。在 do-nothing 运行完成后,由于我们在 kernel command line 上提供了 -q 选项,Pintos 将着手关机。若你好奇 Pintos 如何关机,可以在 GDB 中继续跟踪。

恭喜!你已经在 GDB 中走通了 Pintos 的启动、运行一个 user program 直至完成,以及关机的全过程。自学完成标准:上述书面题已自行作答,且 do-nothingdo-not-muchstack-align-v 三个测试通过。


Deliverables

来源:https://cs162.org/static/proj/proj-pregame/docs/deliverables/

目录

  • Written answers
  • Code
  • Documentation

Written answers

上文 Tasks 各节中编号的书面问题(课上共约 17 题,原交 Gradescope)。自学请自行作答并保存笔记。

Code

src/ 中做出必要的小修复之后,下列测试应全部通过:do-nothingdo-not-muchstack-align-v(在 src/userprog 下用 pintos-testmake check 验证相关结果)。

Documentation

你应当已经通读文档中的必读章节;后续做 Userprog / Threads 时这些概念会反复用到。