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 协议 ,转载请注明出处!