This Is NACHOS#3
本文最后更新于:2022年7月7日 上午
该实验将体验Nachos的用户程序、应用进程进程及Nachos系统调用的相关概念,为后续实验中实现系统调用Exec()与Exit()奠定基础
通过该实验,你需要
- 理解Nachos可执行文件的格式与结构
- 掌握Nachos应用程序的编程语法,了解用户进程是如何通过系统调用与操作系统内核进行交互的
- 掌握如何利用交叉编译生成Nachos的可执行程序
- 理解系统如何为应用程序创建进程,并启动进程
- 理解如何将用户线程映射到核心线程,核心线程执行用户程序的原理与方法
- 理解当前进程的页表是如何与CPU使用的页表进行关联的
Nachos可执行程序的格式
阅读../bin/noff.h

noff文件中包含三个段:
code代码段initData初始化数据段uninitData未初始化数据段
noff文件头中包含用于区分的noffMagic,和三个段在虚拟空间中的起始位置virtualAddr、在文件中的起始位置inFileAddr、段大小size
Nachos应用程序与可执行程序
阅读../test目录下的几个Nachos程序,以../test/halt.c为例

可以看出Nachos应用程序的编程语法为C,引入syscall.h头文件后,直接调用系统调用函数Halt()与操作内核进行交互,终止操作系统。
由于Nachos模拟了一个执行MIPS指令的CPU,因此需要将用户编写的Nachos应用程序编译成MIPS框架的可执行程序。Nachos提供了一个交叉编译程序gcc-2.8.1-mips.tar.gz,可将Nachos用户编写的应用程序编译成MIPS指令集的可执行程序,然后在Nachos中运行。阅读../test/Makefile可知

gcc MIPS交叉编译器将Nachos的应用程序编译成coff格式的可执行文件,然后利用../test/coff2noff将coff格式的可执行程序转换成Nachos CPU可识别的noff可执行程序。
在../test目录中通过下述命令生成halt.c对应的汇编代码halt.s
1 | |
分析该汇编代码,主函数main的栈帧(stack frame)如下创建:
1 | |
主函数main的栈帧如下撤销:
1 | |
C语言程序中的语句对应的机器指令如下注释:
1 | |
在../userprog目录下运行Nachos应用程序halt,加上参数-d m输出显示Nachos模拟的MIPS CPU所执行的每条指令nachos -d m x ../test/halt.noff,运行结果如下:

页表的系统转储
Nachos的存储管理采用分页管理方式,在类AddrSpace中添加成员函数Print(),在为一个应用程序新建一个地址空间后调用该函数,输出该程序的页表(页面与帧的映射关系),有助于后续程序的调试与开发。

测试。在../userprog中运行nachos –x ../test/halt.noff,从输出结果中可以看看程序halt的页面与帧(虚页与实页)的对应关系,以及Nachos为该程序分配的实页数为11

Nachos应用程序的创建与执行
在main.cc中,处理命令行参数-x时,调用../userprog/progtest.cc的StartProcess(char *filename)函数,为用户程序filename创建相应的进程,并启动该进程的执行

阅读该函数并分析

Nachos为应用程序创建进程的过程
首先为程序分配内存空间,将用户程序装入所分配的内存空间,创建相应的页表,建立虚页与实页的映射关系
1 | |
然后将用户进程映射到一个核心线程
1 | |
初始化CPU的寄存器,包括数据寄存器、PC以及栈指针等
1 | |
将用户进程的页表传递给系统核心(Machine类),以便CPU能从用户进程的地址空间中读取应用程序指令
1 | |
最后开始用户进程的执行,Machine::Run()从程序入口开始,完成取指令、译码、执行的过程,直到进程遇到Exit()语句或者异常才退出
1 | |
系统为用户进程分配内存空间、建立页表的过程
阅读函数AddrSpace::AddrSpace(OpenFile* executable)
首先读取可执行文件的文件头,判断是否为noff格式文件

计算该用户程序的代码段、数据段(包括初始化的全局变量与未初始化的全局变量,以及静态变量)、栈空间共需要的页数,判断是否有足够的内存空间分配

建立该用户程序运行进程的页表
Nachos使用的页表中每个页表项结构如下:
int virtualPage:在虚拟内存中的页号int physicalPage:在物理内存中的页号bool valid:有效位,若置1则该页表项有效bool readOnly:只读位,若置1则该页不允许被修改bool use:使用位,当该页被访问或修改时置1bool dirty:脏位,当该页被修改时置1

将整个内存地址空间置0,是为了将未初始化的数据段和栈段置0

将代码段和已初始化的数据段写入内存

理解应用进程如何映射到一个核心线程
线程类Thread维护一个私有变量AddrSpace *space

Thread类的构造函数中,设置space = NULL

当线程与一个应用进程捆绑后,space指向系统为该进程所分配的内存空间,以便被调度时执行该进程所对应的应用程序
理解当前进程的页表是如何与CPU使用的页表进行关联的
系统核心类Machine中维护一个pageTable指针,指向当前正在运行的Nachos应用进程的页表

阅读函数AddrSpace::RestoreState()

直接将machine的pageTable指针指向了刚刚为用户进程创建的页表,实现了当前进程的页表与CPU使用的页表的关联
本文作者: 31
本文链接: http://uuunni.github.io/2022/04/06/This-is-NACHOS-3/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!