Nachos内存管理实现

Nachos内存管理实现

存储器管理包括两个层次的内容:分别是内存管理和文件系统管理。

每个用户进程都是自己的地址空间。当进程运行时它们都会被加载到内存中。

接下来我们仍使用Nachos源代码来讲述地址空间是怎样实现在Nachos模拟的物理内存上实现的。

为了运行一个程序,需要执行一下三步:

1:编译器将各个模块的源代码编译成对应的目标代码模块。

2:连接器将各个目标代码模块链接到一起,形成一个可执行文件。

3:加载器将可执行文件加载到内存中。

每一个目标模块都有自己的代码段,已初始化段,符号表和重定位信息。链接器的任务就是通过在各个模块中符号的交叉引用,将各个目标模块合并成一个模块。

可执行文件中存储的地址是逻辑地址。在程序运行前逻辑地址需要被转换成物理地址。这个转换操作是通过一个叫做MMU的硬件设备执行的。系统可以将模块加载到内存的不同的位置,这可以由重定位寄存器实现。

Nachos中使用machine类对象来模拟运行程序的机器环境。

// machine.h 
//	Data structures for simulating the execution of user programs
//	running on top of Nachos.
//
//	User programs are loaded into "mainMemory"; to Nachos,
//	this looks just like an array of bytes.  Of course, the Nachos
//	kernel is in memory too -- but as in most machines these days,
//	the kernel is loaded into a separate memory region from user
//	programs, and accesses to kernel memory are not translated or paged.
//
//	In Nachos, user programs are executed one instruction at a time, 
//	by the simulator.  Each memory reference is translated, checked
//	for errors, etc.
//
//  DO NOT CHANGE EXCEPT AS NOTED BELOW -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#ifndef MACHINE_H
#define MACHINE_H

#include "copyright.h"
#include "utility.h"
#include "translate.h"

// Definitions related to the size, and format of user memory

const int PageSize = 128; 		// set the page size equal to
					// the disk sector size, for simplicity

//
// You are allowed to change this value.
// Doing so will change the number of pages of physical memory
// available on the simulated machine.
//
const int NumPhysPages = 128;

const int MemorySize = (NumPhysPages * PageSize);
const int TLBSize = 4;			// if there is a TLB, make it small

enum ExceptionType { NoException,           // Everything ok!
		     SyscallException,      // A program executed a system call.
		     PageFaultException,    // No valid translation found
		     ReadOnlyException,     // Write attempted to page marked 
					    // "read-only"
		     BusErrorException,     // Translation resulted in an 
					    // invalid physical address
		     AddressErrorException, // Unaligned reference or one that
					    // was beyond the end of the
					    // address space
		     OverflowException,     // Integer overflow in add or sub.
		     IllegalInstrException, // Unimplemented or reserved instr.
		     
		     NumExceptionTypes
};

// User program CPU state.  The full set of MIPS registers, plus a few
// more because we need to be able to start/stop a user program between
// any two instructions (thus we need to keep track of things like load
// delay slots, etc.)

#define StackReg	29	// User's stack pointer
#define RetAddrReg	31	// Holds return address for procedure calls
#define NumGPRegs	32	// 32 general purpose registers on MIPS
#define HiReg		32	// Double register to hold multiply result
#define LoReg		33
#define PCReg		34	// Current program counter
#define NextPCReg	35	// Next program counter (for branch delay) 
#define PrevPCReg	36	// Previous program counter (for debugging)
#define LoadReg		37	// The register target of a delayed load.
#define LoadValueReg 	38	// The value to be loaded by a delayed load.
#define BadVAddrReg	39	// The failing virtual address on an exception

#define NumTotalRegs 	40

// The following class defines the simulated host workstation hardware, as 
// seen by user programs -- the CPU registers, main memory, etc.
// User programs shouldn't be able to tell that they are running on our 
// simulator or on the real hardware, except 
//	we don't support floating point instructions
//	the system call interface to Nachos is not the same as UNIX 
//	  (10 system calls in Nachos vs. 200 in UNIX!)
// If we were to implement more of the UNIX system calls, we ought to be
// able to run Nachos on top of Nachos!
//
// The procedures in this class are defined in machine.cc, mipssim.cc, and
// translate.cc.

class Instruction;
class Interrupt;

class Machine {
  public:
    Machine(bool debug);	// Initialize the simulation of the hardware
				// for running user programs
    ~Machine();			// De-allocate the data structures

// Routines callable by the Nachos kernel
    void Run();	 		// Run a user program

    int ReadRegister(int num);	// read the contents of a CPU register

    void WriteRegister(int num, int value);
				// store a value into a CPU register

// Data structures accessible to the Nachos kernel -- main memory and the
// page table/TLB.
//
// Note that *all* communication between the user program and the kernel 
// are in terms of these data structures (plus the CPU registers).

    char *mainMemory;		// physical memory to store user program,
				// code and data, while executing

// NOTE: the hardware translation of virtual addresses in the user program
// to physical addresses (relative to the beginning of "mainMemory")
// can be controlled by one of:
//	a traditional linear page table
//  	a software-loaded translation lookaside buffer (tlb) -- a cache of 
//	  mappings of virtual page #'s to physical page #'s
//
// If "tlb" is NULL, the linear page table is used
// If "tlb" is non-NULL, the Nachos kernel is responsible for managing
//	the contents of the TLB.  But the kernel can use any data structure
//	it wants (eg, segmented paging) for handling TLB cache misses.
// 
// For simplicity, both the page table pointer and the TLB pointer are
// public.  However, while there can be multiple page tables (one per address
// space, stored in memory), there is only one TLB (implemented in hardware).
// Thus the TLB pointer should be considered as *read-only*, although 
// the contents of the TLB are free to be modified by the kernel software.

    TranslationEntry *tlb;		// this pointer should be considered 
					// "read-only" to Nachos kernel code

    TranslationEntry *pageTable;
    unsigned int pageTableSize;

    bool ReadMem(int addr, int size, int* value);
    bool WriteMem(int addr, int size, int value);
    				// Read or write 1, 2, or 4 bytes of virtual 
				// memory (at addr).  Return FALSE if a 
				// correct translation couldn't be found.
  private:

// Routines internal to the machine simulation -- DO NOT call these directly
    void DelayedLoad(int nextReg, int nextVal);  	
				// Do a pending delayed load (modifying a reg)

    void OneInstruction(Instruction *instr); 	
    				// Run one instruction of a user program.
    


    ExceptionType Translate(int virtAddr, int* physAddr, int size,bool writing);
    				// Translate an address, and check for 
				// alignment.  Set the use and dirty bits in 
				// the translation entry appropriately,
    				// and return an exception code if the 
				// translation couldn't be completed.

    void RaiseException(ExceptionType which, int badVAddr);
				// Trap to the Nachos kernel, because of a
				// system call or other exception.  

    void Debugger();		// invoke the user program debugger
    void DumpState();		// print the user CPU and memory state 


// Internal data structures

    int registers[NumTotalRegs]; // CPU registers, for executing user programs

    bool singleStep;		// drop back into the debugger after each
				// simulated instruction
    int runUntilTime;		// drop back into the debugger when simulated
				// time reaches this value

    friend class Interrupt;		// calls DelayedLoad()    
};

extern void ExceptionHandler(ExceptionType which);
				// Entry point into Nachos for handling
				// user system calls and exceptions
				// Defined in exception.cc


// Routines for converting Words and Short Words to and from the
// simulated machine's format of little endian.  If the host machine
// is little endian (DEC and Intel), these end up being NOPs.
//
// What is stored in each format:
//	host byte ordering:
//	   kernel data structures
//	   user registers
//	simulated machine byte ordering:
//	   contents of main memory

unsigned int WordToHost(unsigned int word);
unsigned short ShortToHost(unsigned short shortword);
unsigned int WordToMachine(unsigned int word);
unsigned short ShortToMachine(unsigned short shortword);

#endif // MACHINE_H


machine类的主要的成员包括:

用户寄存器:intregisters[NumTotalRegs]

主存:char*MainMemory;

当前进程页表:TranslationEntry*pageTable;

tlb(快表):TranslationEntry*tlb;

机器操作有以下函数模拟:

内存读写:

boolReadMem(intaddr,intsize,int*value);

boolWriteMem(intaddr,intsize,intvalue);

寄存器读写:

intReadRegister(intnum);//readthecontentsofaCPUregister

voidWriteRegister(intnum,intvalue);

指令解析:

voidRun();//Runauserprogram

voidOneInstruction(Instruction*instr);//Runoneinstructionofauserprogram.

异常处理:

ExceptionTypeTranslate(intvirtAddr,int*physAddr,intsize,boolwriting);

run()函数将机器设置成用户模式,并开始一个循环,不停的模拟执行cpu的取指和执行指令的操作。

class Instruction {
  public:
    void Decode();	// decode the binary representation of the instruction

    unsigned int value; // binary representation of the instruction

    char opCode;     // Type of instruction.  This is NOT the same as the
    		     // opcode field from the instruction: see defs in mips.h
    char rs, rt, rd; // Three registers from instruction.
    int extra;       // Immediate or target or shamt field or offset.
                     // Immediates are sign-extended.
};

//----------------------------------------------------------------------
// Machine::Run
// 	Simulate the execution of a user-level program on Nachos.
//	Called by the kernel when the program starts up; never returns.
//
//	This routine is re-entrant, in that it can be called multiple
//	times concurrently -- one for each thread executing user code.
//----------------------------------------------------------------------

void
Machine::Run()
{
    Instruction *instr = new Instruction;  // storage for decoded instruction

    if (debug->IsEnabled('m')) {
        cout << "Starting program in thread: " << kernel->currentThread->getName();
	cout << ", at time: " << kernel->stats->totalTicks << "\n";
    }
    kernel->interrupt->setStatus(UserMode);
    for (;;) {
        OneInstruction(instr);
	kernel->interrupt->OneTick();
	if (singleStep && (runUntilTime <= kernel->stats->totalTicks))
	  Debugger();
    }
}


OneInstruction函数用于模拟一次取指和执行该指令的操作。

93 void

94 Machine::OneInstruction(Instruction *instr)

95 {

96     int raw;

97     int nextLoadReg = 0;

98     int nextLoadValue = 0; // record delayed load operation, to apply

99      // in the future

100

101     // Fetch instruction

102     if (!machine->ReadMem(registers[PCReg], 4, &raw))

103       return; // exception occurred

104     instr->value = raw;

105     instr->Decode();

106

...

122     // Execute the instruction (cf. Kane’s book)

123      switch (instr->opCode) {

124

125      case OP_ADD:

...

547      case OP_UNIMP:

548          RaiseException(IllegalInstrException, 0);

549        return;

550

551      default:

552     ASSERT(FALSE);

553 }

554

555     // Now we have successfully executed the instruction.

556

557     // Do any delayed load operation

558     DelayedLoad(nextLoadReg, nextLoadValue);

559

560     // Advance program counters.

561     registers[PrevPCReg] = registers[PCReg]; // for debugging, in case we

562     // are jumping into lala-land

563     registers[PCReg] = registers[NextPCReg];

564     registers[NextPCReg] = pcAfter;

565 }


用户进程的地址空间是以类AddrSpace对象的方式定义的。每个地址空间对象都有一个指向页表的pageTable指针成员。

Nachos的页表是TranslationEntry类型的数组。

TranslationEntry类型定义如下:

30 class TranslationEntry {

31    public:

32      int virtualPage; // The page number in virtual memory.

33      int physicalPage; // The page number in real memory (relative to the

34      // start of "mainMemory"

35      bool valid; // If this bit is set, the translation is ignored.

36             // (In other words, the entry hasn’t been initialized.)

37     bool readOnly; // If this bit is set, the user program is not allowed

38             // to modify the contents of the page.

39      bool use; // This bit is set by the hardware every time the

40              // page is referenced or modified.

41      bool dirty; // This bit is set by the hardware every time the

42               // page is modified.

43 };


AddrSpace类的构造函数负责创建地址空间。构造函数的参数一个可执行文件。

该函数首先判断文件是否是NOFF文件。接着计算地址空间大小。

// how big is address space?

72 size = noffH.code.size + noffH.initData.size + noffH.uninitData.size

73 + UserStackSize;


然后计算页面数。计算完之后分配页表,并初始化页表项。

page Table = new TranslationEntry[numPages];

87 for (i = 0; i < numPages; i++) {

88    pageTable[i].virtualPage = i; // for now, virtual page # = phys page #

89     pageTable[i].physicalPage = i;

90     pageTable[i].valid = TRUE;

91     pageTable[i].use = FALSE;

92     pageTable[i].dirty = FALSE;

93   pageTable[i].readOnly = FALSE; // if the code segment was entirely on

94 // a separate page, we could set its

95 // pages to be read-only

96 }


此处的初始化页面号等于页框号。

再往后就是将可执行文件的代码段和已初始化数据段复制到内存中。

// then, copy in the code and data segments into memory
103    if (noffH.code.size > 0) {
104      DEBUG(’a’, "Initializing code segment, at 0x%x, size %d\n",
105      noffH.code.virtualAddr, noffH.code.size);
106      executable->ReadAt(&(machine->mainMemory[noffH.code.virtualAddr]),
107      noffH.code.size, noffH.code.inFileAddr);
108     }
109    if (noffH.initData.size > 0) {
110      DEBUG(’a’, "Initializing data segment, at 0x%x, size %d\n",
111      noffH.initData.virtualAddr, noffH.initData.size);
112      executable->ReadAt(&(machine->mainMemory[noffH.initData.virtualAddr]),
113      noffH.initData.size, noffH.initData.inFileAddr);
59
114     }
115


前面介绍了用户程序是怎样加载到物理内存以及如何创建地址空间。但是地址空间中都是包含的逻辑地址。还涉及到将逻辑地址转换成物理地址。系统提供了两种转换方法的接口:线性页面地址转换方法和TLB页
面地址转换方法。在实际的系统中,线性页面地址转换和TLB页面地址转换只能二者取一,目前为简便起见,
Nachos选择了前者,读者可以自行完成TLB页面地址转换的实现。地址转换操作是通过Machine::Translate函数实现的。

该函数首先检查了逻辑地址是否已经对齐。

然后计算虚拟地址所在的页面以及页面偏移。

// calculate the virtual page number, and offset within the page,

207 // from the virtual address

208 vpn = (unsigned) virtAddr / PageSize;

209 offset = (unsigned) virtAddr % PageSize;


得到页面和页面偏移就需要在页表中查找它们所在的物理页框号

如果采用的是TLB转换表,查找TLB表,搜索成功之后获得页表项的指针,取出对应的物理页框号。计算出物理地址:

*physAddr = pageFrame * PageSize + offset;


StartProcess是一个测试函数。它创建一个用户线程,展示了如何使用AddrSpace和Machine类:

23 void

24 StartProcess(char *filename)

25 {

26  OpenFile *executable = fileSystem->Open(filename);

27   AddrSpace *space;

28

29  if (executable == NULL) {

30  printf("Unable to open file %s\n", filename);

31  return;

32  }

33  space = new AddrSpace(executable);

34  currentThread->space = space;

35

36  delete executable; // close file

37

38  space->InitRegisters(); // set the initial register values

39  space->RestoreState(); // load page table register

40

41  machine->Run(); // jump to the user progam

42  ASSERT(FALSE); // machine->Run never returns;

43  // the address space exits

44  // by doing the syscall "exit"

45  }


其实在Nachos提供的源代码中包含了详细的注释,要弄懂Nachos需要不断查阅源代码才行!!

以上参考自《Nachos study book》如有纰漏,请不吝赐教!

2013.1.2于山西大同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值