9) Interrupt::Idle():就绪队列为空调用的代码。检查当前等待处理的中断队列
是否有待处理的中断,如果有将系统时钟前进到下一个被调度的中断进处理。然后处理在新的时刻其他需要发送的中断。如果没有等待的中断,则无事可做,调用Halt()停止;
10) Interrupt::Halt():关掉nachos系统,打印性能数据,最后调用cleanup()
函数;
11) Interrupt::Schedule():安排CPU在某个模拟的时刻被中断,nachos的内核
不能直接调用,只有硬件设备模拟可以调用此代码;
12) Interrupt::CheckIfDue(bool advanceClock):检查一个中断是否要被调度发
生,如果是,则发射中断,并返回true;参数advanceClock是时钟前进标志,为true表示就绪队列为空,系统处于Idle状态,我们应该增加时间到下一个要发射的中断。如果要发生的中断为时间片守护进程,则系统退出: a) 保存旧的状态;
b) 确保中断关闭,以调用中断处理程序; c) 寻找下一个要发生的中断;
d) 如果没有要发生的中断,返回false;
e) 设置了advanceClock,时钟前进到要发生的中断的时刻;
f) 未设置advanceClock,不能前进时钟,则取出的中断放回原处,返回false; g) 当前为空闲模式,要发生的为时钟中断,且等待中断队列没有其他类型的中
断,则将该中断放回原处等待一会处理,返回false; h) 发生中断:
[1]. InHandler设置为true,表示正在进行中断处理程序; [2]. Status设置成系统态,说明中断处理程序是系统态的; [3]. 进行中断处理程序的处理,直到处理结束; [4]. 恢复机器状态,即恢复inHandler和status标志; [5]. 返回true;
13) PrintPending():打印一个将被调度的中断的相关信息,包括时间、地点和原因
(类型);
14) Interrupt::DumpState():打印完整的中断状况,包括状态和将要发生的 C. Machine.h:
1) 模拟运行在nachos上的用户程序的数据结构。用户程序被加载到主存——在
nachos看来,类似于一个byte数组。Nachos的内核也在主存中,但是在现在的很多机器上,内核被加载到一个和用户程序不同的一块内存,而且对内核的访问也不翻译或者分页。
2) 在nachos,用户程序一次执行一条指令。每次对内存的访问都会被翻译和查错。 3) Machine类用来模拟计算机主机。它提供的功能有:读写寄存器。读写主存、运
行一条用户程序的汇编指令、运行用户程序、单步调试用户程序、显示主存和寄存器状态、将虚拟内存地址转换为物理内存地址、陷入 Nachos 内核等等。 4) Machine类实现方法是在宿主机上分配两块内存分别作为虚拟机的寄存器和物理
内存。运行用户程序时,先将用户程序从 Nachos 文件系统中读出,写入模拟的物理内存中,然后调用指令模拟模块对每一条用户指令解释执行。将用户程序的读写内存要求,转变为对物理内存地址的读写。Machine类提供了单步调试用户程序的功能,执行一条指令后会自动停下来,让用户查看系统状态,不过这里的单步调试是汇编指令级的,需要读者对R2/3000指令比较熟悉。如果用户程序想
使用操作系统提供的功能或者发出异常信号时,Machine调用系统异常陷入功能,进入Nachos的核心部分。
5) Nachos寄存器组模拟了全部32个MIPS机(R2/3000)的寄存器,同时加上有关
Nachos系统调试用的8个寄存器,以期让模拟更加真实化并易于调试。 6) class Instruction:模拟机的机器指令由操作代码和操作数组成的;
class Machine: 定义Machine类的目的是为了执行用户程序,如同许多其它系统一样,用户程序不直接使用内存的物理地址,而是使用自己的逻辑地址,在用户程序逻辑地址和实际物理地址之间,就需要一次转换,系统提供了两种转换方法的接口:线性页面地址转换方法和TLB页面地址转换方法。
enum ExceptionType: 在用户程序运行过程中,会有很多系统陷入核心的情况。系统陷入有两大类原因:进行系统调用陷入和系统出错陷入。系统调用陷入在用户程序进行系统调用时发生。系统调用可以看作是软件指令,它们有效地弥补了机器硬件指令不足;系统出错陷入在系统发生错误时发生,比如用户程序使用了非法指令以及用户程序逻辑地址同实际的物理地址映射出错等情况。不同的出错陷入会有不同的处理,比如用户程序逻辑地址映射出错会引起页面的重新调入,而用户程序使用了非法指令则需要向用户报告等等。
7) 另外,由于Nachos可以在多种平台上运行,各种运行平台的数据表达方式不完全
一样,有的是高位在前,有的是高位在后。为了解决跨平台的问题,特地给出了四个函数作数据转换,它们是WordToHost、ShortToHost、WordToMachine和ShortToMachine。
D. Machine.cc:
1) Machine:初始化用户程序执行的模拟(此处实现tlb和linear page table不可
同时使用);
2) ~Machine:释放数据空间,包括maimMemory和tlb;
3) Static void CheckEndian:检查宿主机是大端以便存储(nachos是大端存储); 4) Machine::RaiseException:由于用户程序的系统调用或者发生异常,将控制从用
户模式转换到nachos的内核模式。当系统检查到出错陷入时调用,通过调用ExceptionHandler来处理陷入。(见exception.cc)ExceptionHandler目前对所有的陷入都不进行处理,需要进一步加强。如上面提到的,如果使用TLB表时,应该对PageFaultError作出处理;
5) Machine::Debugger:用户程序的调试工具。注意不可以用gdb,因为gdb没有运
行在nachos上。这里主要是允许单步运行,然后打印内存的内容;
6) Machine::DumpState:打印用户的CPU状态。但是打印主存的内容杀伤力比较大; 7) Machine::ReadRegister/ WriteRegister:读取或者写用户程序寄存器的内容; E. Mipssim.h:
1) 内部的模拟MIPS指令集的数据结构。opCode的名字直接取自MIPS手册,除了
OP_UNIMP(该指令合法但是还未被执行),OP_RES(保留的opcode,体系不支持); 2) struct OpInfo:包括opCode和format,包括I、J、R; 3) opTable:确定指令的opCode和format; 4) specialTable:结合opCode翻译funct域; 5) struct OpString:为了调试打印每条指令的信息; F. Mipssim.cc:
1) 模拟了MIPS R2/3000 进程。这段代码摘自Ousterhout的MIPSSIM包。字节排列
是小端法,所以和Dec的RISC系统兼容;
2) Machine::Run:模拟nachos上用户级程序的执行,当程序开始的时候被内核调用,
从不返回。这段代码是可重入的,因为它可以被同时多次调用,然后每个线程执行各自的用户代码;
3) static int TypeToReg:检索一条指令的寄存器编号; 4) Machine::OneInstruction:执行用户级程序的一条指令;
5) Machine::DelayedLoad:模拟执行一次延迟载入的效果。注意RaiseException和
CheckInterrupts必须调用此函数,因为任何延迟的载入都必须在我们陷入内核前申请;Instruction::Decode:对一条MIPS指令译码;
6) static void Mult:模拟R2000的乘法,指针hiPtr和loPtr在保存double长度
的结果时被重写;
G. Translate.h:
1) 管理虚拟页号到物理页号的翻译,代表页号程序管理物理内存。且此处的数据结
构既用于page table,又用于tlb,条目的格式是<虚拟页号,物理页号>。 2) class TranslationEntry:无论是线性页面地址转换还是TLB页面地址转换,都
需要一张地址转换表,地址转换表是由若干个表项(Entry)组成的。每个表项记录程序逻辑页到内存实页的映射关系,和实页的使用状态、访问权限信息。
H. Translate.cc:
1) 此处支持两种翻译——linear page table, Translation lookaside buffer 2) linear page table:虚拟页号用于到此表的索引,用于查找物理页号; 3) Translation lookaside buffer:和条目的虚拟页号进行匹配,找到的话直接翻
译,否则陷入软件异常;
4) TLB转换页表是硬件来实现的,表的大小一般较实际的用户程序所占的页面数要
小,所以一般TLB表中只存放一部分逻辑页到物理页的转换关系。这样就可能出现逻辑地址转换失败的现象,会发生PageFaultException异常。在该异常处理程序中,就需要借助用户进程空间的线性页面转换表来计算出物理页,同时TLB表中增加一项。如果TLB表已满,就需要对TLB表项做LRU替换;
5) 使用TLB页面转换表处理起来逻辑较线性表为复杂,但是速度要快得多。由于TLB
转换页表是硬件实现的,所以指向TLB转换页表的指针应该是只读的,所以Machine类一旦实例化,TLB指针值不能改动;
6) 在实际的系统中,线性页面地址转换和TLB页面地址转换只能二者取一,目前为
简便起见,Nachos选择了前者;
7) WordToHost/ShortToHost/WordToMachine/ShortToMachine:转换word和short
word使得模拟机器的小端存储。
8) Machine::ReadMem(int addr, int size, int *value):从用户逻辑地址读出size
个字节,转换成相应的类型,存放在value所指向的空间中;
9) Machine::WriteMem(int addr, int size, int value):将value表示的数值根
据size大小转换成相应的机器类型存放在add用户逻辑地址中;
10) Machine::Translate(int virtAddr, int* physAddr, int size, bool writing):
将用户的逻辑地址转换成实际的物理地址,同时需要检查对齐等各种错误
5. /nachos-3.4/code/userprog/:
A. addrspace.h:记录用户程序执行过程的数据结构。用户级CPU状态在执行用户程序的
线程中保存和恢复。UserStackSize定义用户栈空间大小为1024,必须的话可以增加; B. addrspace.cc:
1) 为了运行一个用户程序:
a) 有-N –T 0选项;
b) 运行coff2noff把对象文件转换为nachos的格式(nachos的对象代码格式
就是一个UNIX可执行代码格式的版本); c) 加载NOFF文件到nachos的文件系统;
2) static void SwapHeader:当在一个小端存储的机器上生成代码且正运行在大端
机器上,完成小端到大端的转换工作;
3) AddrSpace::AddrSpace:初始化用户程序空间。从可执行文件加载程序然后设置
开始执行用户指令。先设置从程序存储到物理内存的翻译,nachos最初实现很简单(1:1),因此我们只能是单程序,而且有一个未分段的unsegmented 页表; 4) AddrSpace::~AddrSpace:释放一个地址空间,目前只是删除pageTable; 5) AddrSpace::InitRegisters:设置用户寄存器组的初始值。我们直接写进机器寄
存器,然后就可以直接跳转到用户代码;
6) AddrSpace::SaveState:在上下文切换时保存机器状态,特别是地址空间,目前
什么都不做;
7) AddrSpace::RestoreState:在上下文切换时恢复机器状态,以便于运行地址空间。
目前只是告诉机器如何找到pageTable;
C. bitmap.h:定义了一个bitmap——一个bit数组,每一位可以是on或者off。代表一
个unsigned int数组,用于取模运算找到感兴趣的bit。在管理一些数组元素的分配有用。
在Nachos的文件系统中,是通过位图来管理空闲块的。Nachos的物理磁盘是以扇区为访问单位的,将扇区从0开始编号。所谓位图管理,就是将这些编号填入一张表,表中为0的地方说明该扇区没有被占用,而非0位置说明该扇区已被占用。这部分内容是用BitMap类实现的。
D. progtest.cc:用于测试nachos可以加载一个用户程序并且执行它。也可以测试控制台
的硬件设备。
1) StartProcess:运行一个用户程序。打开可执行文件,加载到主存然后跳转执行。 2)
E. exception.cc:
从用户程序进入nachos内核的入口,有两个事件会导致控制转移给内核:
? syscall:用户代码显式调用内核程序,现阶段只支持halt指令,即系统调用; ? exceptions:用户执行一些CPU不能解决的事情,比如访问不存在的主存,即系
统出错陷入;
中断也可以将控制从用户转到内核,但是在他处处理。
? ExceptionHandler:系统调用用其它异常陷入的入口处理函数,陷入的类型为
SyscallException,且在test目录下的start.s模块中描述了具体的系统调用过程。start.s同ExceptionHandler()配合使用,完成整个调用过程;
? 函数的参数which指明异常类型。函数内部,先取出系统调用的类别码(读第2
个寄存器)放在变量type中,然后如果是系统异常且是SC_Halt,则调用Halt函数,否则出错返回。
F. syscall.h:
nachos系统调用的接口这些函数可以被用户程序调用陷入内核。是nachos内核必须支持的,使得可以运行用户程序。
用户程序只是简单的调用这些程序,汇编语言把系统调用的代码放进寄存器,然后陷入内核。然后调用exception.cc在系统调用的入口处做一些错误检查,最后调用
nachos内核的内核程序。 G.
6. /nachos-3.4/code/filesys/:
A. Filesys.h:
在Nachos中,实现了两套文件系统,它们对外接口是完全一样的:一套称作为FILESYS_STUB,它是建立在UNIX文件系统之上的,而不使用Nachos的模拟磁盘,它主要用于读者先实现了用户程序和虚拟内存,然后再着手增强文件系统的功能;另一套是Nachos的文件系统,它是实现在Nachos的虚拟磁盘上的。当整个系统完成之后,只能使用第二套文件系统的实现。
虽然说 FileSystem中的Create、Open以及Remove方法类似于UNIX操作系统中的creat、open和unlink系统调用。但是在Nachos中,打开和创建文件没有给出打开和创建方式。这是因为在目前的Nachos文件系统中,没有用户分类的概念,也就没有同组用户和其它用户的概念。一个线程打开文件以后,就获得了对该文件操作所有的权利。大多数实用文件系统都提供对文件存取进行保护的功能,保护的一般方法是将用户分类、以及对不同类的用户规定不同的存取权。读者在Nachos提供的文件系统基础上,完全可以添加自己设计和实现的文件保护机制。 B. Filesys.cc:
1) FileSystem:参数format是是否进行格式化的标志。此函数的功能是在同步磁盘
的基础上建立一个文件系统。当format标志设置时,建立一个新的文件系统;否则使用原来文件系统中的内容。
2) Create:在当前的文件系统中创建一个固定大小的文件,在以下4种情况会失败:
? 根目录中已经存在该文件名 ? 文件头 file header 申请不到空间 ? 在目录文件中申请不到文件条目空间 ? 申请不到数据块空间
显然,在Nachos的文件系统中,对目录对象和位图对象的操作应该是临界区操作。因为如果两个线程同时需要向同一个目录中写入一个文件,可能会出现两个线程同时申请到同一个目录项;在分配空闲块时,也会出现相类似的情况。但是目前Nachos没有对此进行处理。
3) Open:在当前的文件系统中删除一个已有的文件。为了打开一个文件,首先在根
目录下查找该文件的 file header,将 header加载到内存。
4) Remove:在当前的文件系统中删除一个已有的文件。需要删除根目录下对应条目,
header 占用空间,数据块占用空间。最后需要把对位图和目录的修改写回磁盘。 5) List:列出文件系统中所有的文件,即根目录下所有文件。
6) Print:打印文件系统的位图、根目录、所有的文件以及文件的 header和数据内
容。
C. filehdr.h:
文件头实际上就是UNIX文件系统中所说的inode结构,它给出一个文件除了文件名之外的所有属性,包括文件长度、地址索引表等等(文件名属性在目录中给出)。所谓索引表,就是文件的逻辑地址和实际的物理地址的对应关系。
Nachos的文件头可以存放在磁盘上,也可以存放在宿主机内存中。在磁盘上存放时一个文件头占用一个独立的扇区。文件头被简单的组织为一个指向数据块的指针表。 Nachos文件头的索引表只有直接索引,没有间接寻址,所以限制文件的最大长度为4K bytes。
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库nachos代码阅读--xq(3)在线全文阅读。
相关推荐: