Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx

上传人:b****4 文档编号:4416678 上传时间:2022-12-01 格式:DOCX 页数:14 大小:24.03KB
下载 相关 举报
Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx_第1页
第1页 / 共14页
Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx_第2页
第2页 / 共14页
Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx_第3页
第3页 / 共14页
Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx_第4页
第4页 / 共14页
Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx

《Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx》由会员分享,可在线阅读,更多相关《Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx(14页珍藏版)》请在冰豆网上搜索。

Nachos 实验11 设计并实现用户空间的虚拟内存管理上.docx

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.

intinFileAddr;  //Theaddressofthissegmentofdatainthefile.

//对于vmcode、vminitData,inFileAddr代表在源文件中的addr

//对应于图1中的linux系统下的文件*.noff.

//对于vmuninitData、vmuserStack,inFileAddr代表在SWAP文件中的位置

PageTypetype;  //Thetypeofthisentry.

//标明页中数据的类型

};

为了实现虚拟内存的页置换,在以上类中增加一个该页在文件中块偏移量inFileAddr和当前页存储的数据的类型的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*SwapBitmap记录SWAP各个页的使用情况,所以,在protest.cc中添加声明:

BitMap*Mmbmp=newBitMap(NumPhysPages);//bitmapforallocatingofphysicalpagesinfjkdjkmainMemory.

BitMap*SwapBitmap=newBitMap(NumPhysPages);//bitmapforSWAPfile,

        //assumethesizeofSWAPfileisNumPhyPages.

OpenFile*SwapFile=fileSystem->Open("SWAP");//stubinNachos_Linux

       //在lab11的目录下建立文件SWAP

NoffHeader

修改原有的结构体NoffHeader为类类型,目的是为了能够将NoffHeader作为AddrSpace类的私有实例变量存取,结构体无法实例化为类的私有变量,所以将结构体NoffHeader重写,变为类NoffHeader,并一起更改结构体NoffSegment为类类型。

两者的功能在保证原结构体功能的基础上,为了调试和输出方便,添加输出函数Print()。

具体定义如下:

#defineNOFFMAGIC0xbadfad

classNoffSegment

{

public:

intvirtualAddr;

intinFileAddr;

intsize;

voidPrint();

NoffSegment();

~NoffSegment();

};

classNoffHeader

{

public:

intnoffMagic;

NoffSegmentcode;

NoffSegmentinitData;

NoffSegmentuninitData;

voidPrint();

NoffHeader();

~NoffHeader();

};

AddrSpace

扩展原有的AddrSpace的属性:

添加属性——当下正在执行的用户文件的指针OpenFile*executable,因为我们无法一次读取所有需要的数据,更多情况下,我们边用边读,所以设置一个变量executable来保存指向用户文件的指针。

添加属性——当下正在执行的用户文件的NoffHeader,因为NoffHeader在初始化时,将会加载到mainMemory的0号地址中,一旦程序运行之后,原0号地址中的内容必定会被用户程序重写,但因为我采用的是bitmap做物理地址与虚地址的变换,其中的变换细节要求需要在进行物理和虚拟页变换时知道code的virtualAddr,initData的virtualAddr等的数据,(详细细节见AddrSpace:

:

Translate介绍)所以为了访问方便,设置其为用户进程的一个属性。

添加virtualMem数组和p_vm指针,用来实现FIFO算法。

virtualMem存储的是按进入内存的先后顺序排列的当前占用内存空间的虚页,p_vm指针指向数组中当前将要被换出的那个位置。

(详细说明见AddrSpace:

:

FIFO介绍)

private:

TranslationEntry*pageTable;//Assumelinearpagetabletranslationfornow!

unsignedintnumPages;//Numberofpagesinthevirtualaddressspace

OpenFile*executable;//Apointertotheexecutingfile

NoffHeadernoffH;  //TheheaderoftheOpenFileexecutable

intvirtualMem[MemPages];//Storevirtualpagesofthepagesinthemainmemory

intp_vm;    //Thepointertonextmemorytoswapout

添加AddrSpace实现用户空间虚拟内存的函数:

voidInitPageTable();//用于初始化AddrSpace的pageTable的基本信息

voidInitInFileAddr();//初始化pageTable中各个entry的inFileAddr、type

voidFIFO(intnewPage);//调用translate和swap实现先进先出的虚拟内存置换算法

voidTranslate(intaddr,unsignedint*vpn,unsignedint*offset);

      //将addr对应的虚拟页页号vpn和页内偏移量offset计算出来

voidSwap(intoldPage,intnewPage);//调用WriteBack和ReadIn

//实现将mainMemory中的oldPage替换成newPage

voidWriteBack(intoldPage);//将oldPage这一个页写回

    //code和initData将会被写回文件;

//uninitData和userStack内容将会被写回交换区SWAP

voidReadIn(intnewPage);//将newPage写入到mainMemory

    //code和initData将通过inFileAddr从文件中读出;

    //uninitData和userStack或从交换区SWAP读出,或只是将mainMemory中分配到的地址段清零

关键源代码及注释

首先,简要说明一下现在Nachos系统的虚拟存储功能的能力。

为了简便起见,规定系统默认给每个用户进程分配MemPages大小的主存,当用户的进程装入内存,进行数据初始化的时候,按照用户程序在pageTable中的存储顺序从前向后装入MemPages大小的页到内存中去。

在用户进程在运行的过程之中,如果访问内存无法找到想要的virtualAddr,那么采用FIFO策略进行不同页之间的切换。

那么接下来,按照一个用户进程在Nachos下执行的过程顺序对本次实验的程序进行解剖说明。

用户进程(pageTable)的初始化

用户程序从progtest.cc的StartProcess接口开始装载,通过传递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))

   SwapHeader(&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

InitInFileAddr();[3]

Print();    

}

[1]在原Nachos的progtest.cc中可以看到,当使用executable完成AddrSpacespace的初始化工作后,采用了直接“deleteexecutable”的语句,将文件关闭,但是在进行WriteBack和ReadIn函数调用的过程中,仍然需要诸如“executable->WriteAt(&(machine->mainMemory[pageTable[oldPage].physicalPage*PageSize]),PageSize,pageTable[oldPage].inFileAddr);”的语句,所以注释掉progtest.cc中的delete语句,将exectuable的指针传递给AddrSpace,令AddrSpace的属性保存控制executable。

[2]调用新添加到AddrSpace类中的函数InitPageTable,这个函数主要作用是完成初始化原pageTable的基本信息,这些信息都是不需要根据此Entry的type或者是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].inFileAddr=-1;

//初始化inFileAddr为-1,具体值会在InitFileAddr中计算出。

  if(i>=numPages-StackPages)

   pageTable[i].type=vmuserStack;//wecanbesurethatthestack

//mustbelocatedinthelastStackPagespages,

//thusinitiatethetypeofstackpages.

  //最后,将前MemPages个虚页的内容分配mainMemory的物理页,准备将其写入到mainMemory中去,写入过程有InitFileAddr完成。

  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中的函数,作用是初始化各个page的inFileAddr、type,并将已经分配了物理页的page写入到mainMemory,通过调用[2][3],完成整个pageTable的初始化工作。

void

AddrSpace:

:

InitInFileAddr()

{

   if(noffH.code.size>0)

   {

  unsignedintnumP=divRoundUp(noffH.code.size,PageSize);

  

  for(inti=0;i

  {

   pageTable[i].inFileAddr=noffH.code.inFileAddr+i*PageSize;

   pageTable[i].type=vmcode;

   if(pageTable[i].valid)

   {

  executable->ReadAt(&(machine->mainMemory[pageTable[i].physicalPage*PageSize]),PageSize,pageTable[i].inFileAddr);

//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].inFileAddr=noffH.initData.inFileAddr+(i-firstP)*PageSize;

   pageTable[i].type=vminitData;

   if(pageTable[i].valid)

   {

  executable->ReadAt(&(machine->mainMemory[pageTable[i].physicalPage*PageSize]),PageSize,pageTable[i].inFileAddr);

//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();*/}

  }

}

}

需要说明的是,在调用完InitInFileAddr函数之后,各中类型的pageTable的entry的type和inFileAddr对应关系如下:

typeinFileAddr

vmcodenoffH.code.inFileAddr+i*PageSize

vminitDatanoffH.initData.inFileAddr+(i-firstP)*PageSize

vmuninitData-1

vmuserStack-1

请注意,最后两项vmuninitData和vmuserStack中inFileAddr的值初始化时都被定义为-1,但它们都有可能在程序执行的过程中被赋予大于0的值,当inFileAddr值大于0时,表明此页已经被修改过,修改过的页被写在SWAP交换区文件中的inFileAddr位置处。

缺页异常处理

用户进程在初始化完成后,正式开始执行,在执行的过程中必然会出现mipscpu想要访问某个初始化时并不在mainMemory中的virtualAddr,此时,发生缺页异常后,会进入userprog/exception.cc中的ExceptionHandler函数,并且MIPSCPU想访问的那个虚拟地址在寄存器BadVAddrReg中。

此时,只需使这个虚页进入主存,然后令MIPSCPU重新执行原来那条指令。

在处理缺页异常之前,我

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1