Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx
- 文档编号:8253958
- 上传时间:2023-01-30
- 格式:DOCX
- 页数:10
- 大小:21.47KB
Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx
《Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx》由会员分享,可在线阅读,更多相关《Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx(10页珍藏版)》请在冰豆网上搜索。
Nachos实验11设计并实现用户空间的虚拟内存管理上
实验目
在未实现用户空间虚拟内存管理之前,Nachos系统在运行一个用户进程时候,需要将程序在运行时可能会用到所有信息都拷贝到mainMemory中去。
这样,因为mainMemory大小限制,一些较大文件可能无法执行;而相对应,一些程序中可能包含着大量在执行过程中极少或根本不会被访问数据,这些数据却又长期占据了内存资源。
本次试验目:
整体理解Nachos系统组织结构。
设计并实现用户空间虚拟内存管理。
实验环境
Linux操作系统,Nachos操作系统
实验分析
此次实验是在实验7-8——ExtensionofAddrSpaceandSystemCallsExec()基础上更改。
实验目录并没有在系统已有vm目录下进行,而是将实验目录lab7-8更名为lab11,目是使用lab7-8目录下Makefile文件。
在本次实验过程中,发现并更改了实验7-8一些疏漏之处。
为了说明方便,首先澄清一下基本概念和数据结构:
用bitmap做物理地址分配
图1存取关系图
页表
classTranslationEntry{
public:
intvirtualPage;//Thepagenumberinvirtualmemory.
//对应于图1中虚页
intphysicalPage;//Thepagenumberinrealmemory(relativetothe
//startof"mainMemory"
//对应于图1中物理页
boolvalid; //Ifthisbitisset,thetranslationisignored.
//(Inotherwords,theentryhasn'tbeeninitialized.)
boolreadOnly;//Ifthisbitisset,theuserprogramisnotallowed
//tomodifythecontentsofthepage.
booluse; //Thisbitissetbythehardwareeverytimethe
//pageisreferencedormodified.
booldirty; //Thisbitissetbythehardwareeverytimethe
//pageismodified.
intin; //Theaddressofthissegmentofdatainthefile.
//对于vmcode、vminitData,in代表在源文件中addr
//对应于图1中linux系统下文件*.noff.
//对于vmuninitData、vmuserStack,in代表在SWAP文件中位置
PageTypetype; //Thetypeofthisentry.
//标明页中数据类型
};
为了实现虚拟内存页置换,在以上类中增加一个该页在文件中块偏移量in和当前页存储数据类型type。
其中type类型PageType定义为枚举类型,写在文件translate.h中。
enumPageType
{vmcode,vminitData,vmuninitData,vmuserStack};
分别代表此页数据为代码,初始化数据,未初始化数据,用户栈。
交换区SWAP
曾在实验7-8中,在progtest.cc文件中声明了BitMap*Mmbmp(如图1),记录mainMemory中物理页分配情况。
它位置表明了,此Mmbmp作用域是整个Nachos系统,它不隶属于任何一个用户进程。
当然,我们可以实现一个更好方式:
将Mmbmp放到Machine中,但是这要修改Machine定义,如果查看Machine类定义就可以知道,Machine牵扯到Nachos核心系统控制,为了尽量保证Nachos系统稳定性,则将BitMap*Mmbmp作为全局变量放在了progtest.cc中。
同样道理,将交换区文件SWAP生命周期与Mmbmp相似,同时SWAP也需要一个BitMap*S记录SWAP各个页使用情况,所以,在protest.cc中添加声明:
BitMap*Mmbmp=newBitMap(NumPhysPages);//bitmapforallocatingofphysicalpagesinfjkdjkmainMemory.
BitMap*S=newBitMap(NumPhysPages);//bitmapforS,
//assumethesizeofSisNumPhyPages.
OpenFile*S=>Open("SWAP");//stubinNachos_Linux
//在lab11目录下建立文件SWAP
NoffHeader
修改原有结构体NoffHeader为类类型,目是为了能够将NoffHeader作为AddrSpace类私有实例变量存取,结构体无法实例化为类私有变量,所以将结构体NoffHeader重写,变为类NoffHeader,并一起更改结构体NoffSegment为类类型。
两者功能在保证原结构体功能基础上,为了调试和输出方便,添加输出函数Print()。
具体定义如下:
#defineNOFFMAGIC0xbadfad
classNoffSegment
{
public:
intvirtualAddr;
intin;
intsize;
voidPrint();
NoffSegment();
~NoffSegment();
};
classNoffHeader
{
public:
intnoffMagic;
NoffSegmentcode;
NoffSegmentinitData;
NoffSegmentuninitData;
voidPrint();
NoffHeader();
~NoffHeader();
};
AddrSpace
扩展原有AddrSpace属性:
添加属性——当下正在执行用户文件指针OpenFile*executable,因为我们无法一次读取所有需要数据,更多情况下,我们边用边读,所以设置一个变量executable来保存指向用户文件指针。
添加属性——当下正在执行用户文件NoffHeader,因为NoffHeader在初始化时,将会加载到mainMemory0号地址中,一旦程序运行之后,原0号地址中内容必定会被用户程序重写,但因为我采用是bitmap做物理地址与虚地址变换,其中变换细节要求需要在进行物理和虚拟页变换时知道codevirtualAddr,initDatavirtualAddr等数据,(详细细节见AddrSpace:
:
Translate介绍)所以为了访问方便,设置其为用户进程一个属性。
添加virtualMem数组和p_vm指针,用来实现FIFO算法。
virtualMem存储是按进入内存先后顺序排列当前占用内存空间虚页,p_vm指针指向数组中当前将要被换出那个位置。
(详细说明见AddrSpace:
:
FIFO介绍)
private:
TranslationEntry*pageTable;//Assumelinearpagetabletranslationfornow!
unsignedintnumPages;//Numberofpagesinthevirtualaddressspace
OpenFile*executable;//ApointertotheexecutingnoffH; //TheheaderoftheOpen
intvirtualMem[MemPages];//Storevirtualpagesofthepagesinthemainmemory
intp_vm; //Thepointertonextmemorytos
添加AddrSpace实现用户空间虚拟内存函数:
voidInitPageTable();//用于初始化AddrSpacepageTable基本信息
voidInitIn();//初始化pageTable中各个entryin、type
voidFIFO(intnewPage);//调用translate和swap实现先进先出虚拟内存置换算法
voidTranslate(intaddr,unsignedint*vpn,unsignedint*offset);
//将addr对应虚拟页页号vpn和页内偏移量offset计算出来
voidSoldPage,intnewPage);//调用WriteBack和ReadIn
//实现将mainMemory中oldPage替换成newPage
voidWriteBack(intoldPage);//将oldPage这一个页写回
//code和initData将会被写回文件;
//uninitData和userStack内容将会被写回交换区SReadIn(intnewPage);//将newPage写入到mainMemory
//code和initData将通过in从文件中读出;
//uninitData和userStack或从交换区SWAP读出,或只是将mainMemory中分配到地址段清零
关键源代码与注释
首先,简要说明一下现在Nachos系统虚拟存储功能能力。
为了简便起见,规定系统默认给每个用户进程分配MemPages大小主存,当用户进程装入内存,进行数据初始化时候,按照用户程序在pageTable中存储顺序从前向后装入MemPages大小页到内存中去。
在用户进程在运行过程之中,如果访问内存无法找到想要virtualAddr,那么采用FIFO策略进行不同页之间切换。
那么接下来,按照一个用户进程在Nachos下执行过程顺序对本次实验程序进行解剖说明。
用户进程(pageTable)初始化
用户程序从progtest.ccStartProcess接口开始装载,通过传递OpenFile*executable到AddrSpacespace生成新AddrSpace实例。
此时space进行初始化:
AddrSpace:
:
AddrSpace(OpenFile*exe)
{
unsignedintsize;
executable=exe;[1]
executable->ReadAt((char*)&noffH,sizeof(noffH),0);
if((noffH.noffMagic!
=NOFFMAGIC)&&
(WordToHost(noffH.noffMagic)==NOFFMAGIC))
S(&noffH);
ASSERT(noffH.noffMagic==NOFFMAGIC);
numPages=divRoundUp(noffH.code.size,PageSize)+divRoundUp(noffH.initData.size,PageSize)
+divRoundUp(noffH.uninitData.size,PageSize)+StackPages;
size=(MemPages+StackPages)*PageSize;
//加粗语句决定了在给定虚拟地址addr,换算虚页vpn和页内偏移量offset时不再是
//vpn=(unsigned)virtAddr/PageSize;
//offset=(unsigned)virtAddr%PageSize;
//具体转换令写函数AddrSpace:
:
Translate实现
printf("numPagesis%d\n",numPages);
printf("numPages=%d+%d+%d+%d\n",divRoundUp(noffH.code.size,PageSize),divRoundUp(noffH.initData.size,PageSize),
divRoundUp(noffH.uninitData.size,PageSize),StackPages);
DEBUG('a',"Initializingaddressspace,numpages%d,size%d\n",
numPages,size);
//zeroouttheentireaddressspace,tozerotheunitializeddatasegment
//andthestacksegment
bzero(machine->mainMemory,size);
//first,setupthetranslation
InitPageTable();[2]
//then,copyinthecodeanddatasegmentsintomemory
InitIn();[3]
Print();
}
[1]在原Nachosprogtest.cc中可以看到,当使用executable完成AddrSpacespace初始化工作后,采用了直接“deleteexecutable”语句,将文件关闭,但是在进行WriteBack和ReadIn函数调用过程中,仍然需要诸如“executable->WriteAt(&(machine->mainMemory[pageTable[oldPage].physicalPage*PageSize]),PageSize,pageTable[oldPage].in);”语句,所以注释掉progtest.cc中delete语句,将exectuable指针传递给AddrSpace,令AddrSpace属性保存控制executable。
[2]调用新添加到AddrSpace类中函数InitPageTable,这个函数主要作用是完成初始化原pageTable基本信息,这些信息都是不需要根据此Entrytype或者是NoffHeader就可以直接确定信息:
virtualPage,use,dirty,readOnly,valid,physicalPage等。
void
AddrSpace:
:
InitPageTable()
{
p_vm=0;
pageTable=newTranslationEntry[numPages];
for(inti=0;i { pageTable[i].virtualPage=i;//fornow,virtualpage#=physpage# pageTable[i].use=FALSE; pageTable[i].dirty=FALSE; pageTable[i].readOnly=FALSE;//ifthecodesegmentwasentirelyon //aseparatepage,wecouldsetits //pagestoberead-only pageTable[i].in=-1; //初始化in为-1,具体值会在Init中计算出。 if(i>=numPages-StackPages) pageTable[i].type=vmuserStack;//wecanbesurethatthestack //mustbelocatedinthelastStackPagespages, //thusinitiatethetypeofstackpages. //最后,将前MemPages个虚页内容分配mainMemory物理页,准备将其写入到mainMemory中去,写入过程有Init完成。 if(i { virtualMem[p_vm]=pageTable[i].virtualPage;//==i p_vm=(p_vm+1)%MemPages; pageTable[i].physicalPage=Mmbmp->Find(); pageTable[i].valid=TRUE; } else{ pageTable[i].physicalPage=-1; pageTable[i].valid=FALSE; } } } [3]新添加到AddrSpace中函数,作用是初始化各个pagein、type,并将已经分配了物理页page写入到mainMemory,通过调用[2][3],完成整个pageTable初始化工作。 void AddrSpace: : InitIn() { if(noffH.code.size>0) { unsignedintnumP=divRoundUp(noffH.code.size,PageSize); for(inti=0;i { pageTable[i].in=noffH.code.in+i*PageSize; pageTable[i].type=vmcode; if(pageTable[i].valid) { executable->ReadAt(&(machine->mainMemory[pageTable[i].physicalPage*PageSize]),PageSize,pageTable[i].in); //Ifthepagehasbeenallocatedwithphysicalpage,readintothemainMemory } } } if(noffH.initData.size>0) { unsignedintnumP,firstP; numP=divRoundUp(noffH.initData.size,PageSize); firstP=divRoundUp(noffH.initData.virtualAddr,PageSize); for(inti=firstP;i { pageTable[i].in=noffH.initData.in+(i-firstP)*PageSize; pageTable[i].type=vminitData; if(pageTable[i].valid) { executable->ReadAt(&(machine->mainMemory[pageTable[i].physicalPage*PageSize]),PageSize,pageTable[i].in); //Ifthepagehasbeenallocatedwithphysicalpage,readintothemainMemory } } } if(noffH.uninitData.size>0) { unsignedintnumP,firstP; numP=divRoundUp(noffH.uninitData.size,PageSize); firstP=divRoundUp(noffH.uninitData.virtualAddr,PageSize); for(inti=firstP;i { pageTable[i].type=vmuninitData; if(pageTable[i].valid) {/*brzero();*/} } } } 需要说明是,在调用完InitIn函数之后,各中类型pageTableentrytype和in对应关系如下: typein vmcodenoffH.code.in+i*PageSize vminitDatanoffH.initData.in+(i-firstP)*PageSize vmuninitData-1 vmuserStack-1 请注意,最后两项vmuninitData和vmuserStack中in值初始化时都被定义为-1,但它们都有可能在程序执行过程中被赋予大于0值,当in值大于0时,表明此页已经被修改过,修改过页被写在SWAP交换区文件中in位置处。 缺页异常处理 用户进程在初始化完成后,正式开始执行,在执行过程中必然会出现mipscpu想要访问某个初始化时并不在mainMemory中virtualAddr,此时,发生缺页异常后,会进入userprog/exception.cc中ExceptionHandler函数,并且MIPSCPU想访问那个虚拟地址在寄存器BadVAddrReg中。 此时,只需使这个虚页进入主存,然后令MIPSCPU重新执行原来那条指令。 在处理缺页异常之前,我们应首先注意到,cpu访问内存时候用是virtualAddr,而非virtualPage,我们打开translate.ccMachine: : Translate方法就可以发现,cpu进行virtualAddr翻译成虚页号vpn和页内偏移量offset时使用默认方法是 vpn=(unsigned)virtAddr/PageSize; offset=(unsigned)virtAddr%PageSize; 但是这样翻译得到结果,并不等价于我们pageTablevirtualPage(即pageTable数组下标号),回想我们设计在code,initData,uninitData,userStack之间两两存在着碎片。 所以为了让cpu明确地知道它究竟想要访问页是哪一个,在AddrSpace中添加方法Translate: void AddrSpace: : Translate(intaddr,unsignedint*vpn,unsignedint*offset) { intpage=-1; into
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Nachos 实验11 设计并实现用户空间的虚拟内存管理上 实验 11 设计 实现 用户 空间 虚拟内存 管理