NTU Operating System Project 3

NTU Operating System Project 3

tags: NTU_OS Operating System NachOS Memory Management

[TOC]

Description First

I used the code from Project2 directly and obtained the correct value, 1 and 7220, by the command ./nachos -e ../test/sort -e ../test/matmult respectively. After debugging a while, I knew what’s the matter. In project2, I set const unsigned int NumPhysPages = 256; so that it can handle huge computing resource. In order to address this project, we must change it back to 32.

Motivation

  • Normally speaking, if you follow the command ./nachos -e ../test/matmult and ./nachos -e ../test/sort in Project2, you’ll get
    1
    2
    3
    4
      Total threads number is 1
      Thread ../test/sort is executing.
      Assertion failed: line 118 file ../userprog/addrspace.cc
      Aborted (core dumped)
    
    1
    2
    3
    4
      Total threads number is 1
      Thread ../test/matmult is executing.
      Assertion failed: line 118 file ../userprog/addrspace.cc
      Aborted (core dumped)
    
  • Our goal is to obtain the correct outcome 1 and 7220 from /test/sort and /test/matmult respectively that shown as below
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
      $ ./nachos -e ../test/sort -e ../test/matmult
      Total threads number is 2
      Thread ../test/sort is executing.
      Thread ../test/matmult is executing.
      return value:7220
      return value:1
      No threads ready or runnable, and no pending interrupts.
      Assuming the program completed.
      Machine halting!
    
      Ticks: total 43818400, idle 40, system 4381880, user 39436480
      Disk I/O: reads 0, writes 0
      Console I/O: reads 0, writes 0
      Paging: faults 0
      Network I/O: packets received 0, sent 0
    
  • Now, let’s try to analyze the problem. NachOS has physical memory limitation, so that it cannot handle the program that needs huge memory resource such as sort.c and matmult.c. Thus, we can utilize Demand Paging to address it → realize a virtual memory if the main memory has no space

    When the space in main memory is enough, pages will be stored in main memory. If the space is insufficient, the pages and data will swap out. When there is valid space, i.e. the space is released, and the data is needed, the pages and data will then swap in main memory. The scheduling method of the page replacement algorithm is implemented by LRU(Least Recently Used). - by wangssuming

Implementation

  • /code/userprog/userkernel.h create a new SynchDisk called SwapDisk and replace debugUserProg from private to public
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
      class UserProgKernel : public ThreadedKernel
      {
          public:
              ...
              /*-----------------------Homework for Memory Management------------------------*/
              // Create a new SynchDisk called SwapDisk to simulate the secondary storage
              SynchDisk *SwapDisk;     // SwapDisk saves pages if main memory is not enough
              bool debugUserProg;		// single step user program
              /*-----------------------Homework for Memory Management------------------------*/
              Machine *machine;
              FileSystem *fileSystem;
              ...
          private:
              ...
              /*-----------------------Homework for Memory Management------------------------*/
              // No longer needed for HW3
              //bool debugUserProg;		// single step user program
              /*-----------------------Homework for Memory Management------------------------*/
              Thread* t[10];
              ...
      };
    
  • /code/userprog/userkernel.cc Initialize SwapDisk
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
      void UserProgKernel::Initialize()
      {
          /*-----------------------Homework for Memory Management------------------------*/
          ThreadedKernel::Initialize(RR);
          // Initialized SwapDisk
          machine = new Machine(debugUserProg);
          fileSystem = new FileSystem();
          SwapDisk = new SynchDisk("New SwapDisk");// Swap disk for virtual memory
          #ifdef FILESYS
              synchDisk = new SynchDisk("New SynchDisk");
          #endif // FILESYS
          /*-----------------------Homework for Memory Management------------------------*/
      }
      void UserProgKernel::Initialize(SchedulerType type)//
      {
          ...
          /*-----------------------Homework for Memory Management------------------------*/
          SwapDisk = new SynchDisk("New SwapDisk");// Swap disk for virtual memory
          /*-----------------------Homework for Memory Management------------------------*/
          #ifdef FILESYS
          ...
      };
    
  • /code/machine/machine.h Defined lots of variable in Machine class contained UsedPhyPage to record whether the physical memory is used or not, UsedVirtualPage to record whether the virtual memory is used or not, etc.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
      class Instruction;
      class Interrupt;
    
      class Machine
      {
          public:
              ...
              TranslationEntry *pageTable;
              unsigned int pageTableSize;
              bool ReadMem(int addr, int size, int* value);
              /*-----------------------Homework for Memory Management------------------------*/
              bool UsedPhyPage[NumPhysPages]; //record the pages in the main memory
              bool UsedVirtualPage[NumPhysPages]; //record the pages in the virtual memory
              int ID_number; // machine ID
              int PhyPageInfo[NumPhysPages]; //record physical page info (ID)
              TranslationEntry *main_tab[NumPhysPages]; // pagetable
              /*-----------------------Homework for Memory Management------------------------*/
          private:
              ...
      };
    
  • /code/userprog/addrspace.cc
    • In line <font color=#FF0000>45</font>

      Since we are handling virtual memory, the ASSERT to guarantee the number of pages does not exceed the number of physical pages in main memory in /code/userprog/addrspace.cc is no longer needed. - by pai445

    • The for loop in line <font color=#FF0000>79</font> is used to find an available page for current process. The while loop in line <font color=#FF0000>82</font> is as similar as project2 that used variable j to find free memory page.

      There are two different cases in the following. When the main memory still have empty frame, then we can put the page into the main memory and update the information to the page table. This step is achieved by the function ReadAt. The other case will be the main memory is fulled. Then we have check the available virtual memory space by the similar while loop and write the page in to SwapDisk by the WriteSector function. - by pai445

    • Execute function in line <font color=#FF0000>135</font> and SaveState in line <font color=#FF0000>147</font> We use a flag, pageTable_is_load, defined in /code/userprog/addrspace.h to check whether the page table is successfully loaded to make the context-switch work. ```cpp=1 #define PAGE_OCCU true #define PAGE_FREE false /———————–Homework for Memory Management————————/ // There’s no longer needed in HW3 // bool AddrSpace::PhyPageStatus[NumPhysPages] = {PAGE_FREE}; // int AddrSpace::NumFreePages = NumPhysPages; /———————–Homework for Memory Management————————/ AddrSpace::AddrSpace() { /———————–Homework for Memory Management————————/ ID=(kernel->machine->ID_number)++; kernel->machine->ID_number=(kernel->machine->ID_number)++; /———————–Homework for Memory Management————————/ … }

    AddrSpace::~AddrSpace() { // Free the physical page that this program used /———————–Homework for Memory Management————————/ // No longer needed for HW3 // for(int i = 0; i < numPages; i++) // { // AddrSpace::PhyPageStatus[pageTable[i].physicalPage] = PAGE_FREE; // AddrSpace::NumFreePages++; // } /———————–Homework for Memory Management————————/ delete pageTable; }

    bool AddrSpace::Load(char *fileName) { OpenFile *executable = kernel->fileSystem->Open(fileName); NoffHeader noffH; unsigned int size;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
      /*-----------------------Homework for Memory Management------------------------*/
      unsigned int tmp;
      /*-----------------------Homework for Memory Management------------------------*/
      ...
      size = numPages * PageSize;
    
      /*-----------------------Homework for Memory Management------------------------*/
      /* For HW3 is no longer needed  */
      // ASSERT(numPages <= NumPhysPages);	// check we're not trying
                                                  // to run anything too big --
                                                  // at least until we have
                                                  // virtual memory
    
      // Allocate
      pageTable = new TranslationEntry[numPages];
      // No longer needed for HW3
      /*for(unsigned int i = 0, idx = 0; i < numPages; i++)
      {
          pageTable[i].virtualPage = i;
          while(idx < NumPhysPages && AddrSpace::PhyPageStatus[idx] == PAGE_OCCU) idx++;
          AddrSpace::PhyPageStatus[idx] = PAGE_OCCU;
          AddrSpace::NumFreePages--;
          // Clean the page that'll be used soon
          bzero(&kernel->machine->mainMemory[idx * PageSize], PageSize);
          pageTable[i].physicalPage = idx;
          pageTable[i].valid = true;
          pageTable[i].use = false;
          pageTable[i].dirty = false;
          pageTable[i].readOnly = false;
      }*/
      /*-----------------------Homework for Memory Management------------------------*/
    
      DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size);
      if (noffH.code.size > 0)
      {
          /*-----------------------Homework for Multithread------------------------*/;
          // For HW3 is no longer needed
          // DEBUG(dbgAddr, "Initializing code segment.");
          // DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size)
          // executable->ReadAt(&(kernel->machine->mainMemory[pageTable[noffH.code.virtualAddr/PageSize].physicalPage * PageSize + (noffH.code.virtualAddr%PageSize)]), noffH.code.size, noffH.code.inFileAddr);
          // executable->ReadAt(&(kernel->machine->mainMemory[noffH.code.virtualAddr]), noffH.code.size, noffH.code.inFileAddr);
    
          for(unsigned int j=0,i=0;i < numPages ;i++)
          {
              j=0;
              while(kernel->machine->UsedPhyPage[j]!=FALSE&&j<NumPhysPages){j++;}
    
              // main memory is enough, put the page to main memory
              if(j<NumPhysPages)
              {   
                  kernel->machine->UsedPhyPage[j]=TRUE;
                  kernel->machine->PhyPageInfo[j]=ID;
                  kernel->machine->main_tab[j]=&pageTable[i];
                  pageTable[i].physicalPage = j;
                  pageTable[i].valid = TRUE;
                  pageTable[i].use = FALSE;
                  pageTable[i].dirty = FALSE;
                  pageTable[i].readOnly = FALSE;
                  pageTable[i].ID =ID;
                  pageTable[i].LRU_counter++; // LRU counter when save in memory
                  executable->ReadAt(&(kernel->machine->mainMemory[j*PageSize]),PageSize, noffH.code.inFileAddr+(i*PageSize));
              }
              // main memory is not enough, use virtual memory
              else
              { 
                  char *buffer;
                  buffer = new char[PageSize];
                  tmp=0;
                  while(kernel->machine->UsedVirtualPage[tmp]!=FALSE){tmp++;}
                  kernel->machine->UsedVirtualPage[tmp]=true;
                  pageTable[i].virtualPage=tmp; //record the virtual page we save 
                  pageTable[i].valid = FALSE; //not load in main memory
                  pageTable[i].use = FALSE;
                  pageTable[i].dirty = FALSE;
                  pageTable[i].readOnly = FALSE;
                  pageTable[i].ID =ID;
                  executable->ReadAt(buffer,PageSize, noffH.code.inFileAddr+(i*PageSize));
                  kernel->SwapDisk->WriteSector(tmp,buffer); // write in virtual memory (SwapDisk)
              }
          }
          /*-----------------------Homework for Memory Management------------------------*/
      }
      /*-----------------------Homework for Memory Management------------------------*/
      if (noffH.initData.size > 0)
      {
          // For HW1, it's needed, but not in HW3
          // DEBUG(dbgAddr, "Initializing data segment.");
          // DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size);
          // executable->ReadAt(&(kernel->machine->mainMemory[pageTable[noffH.initData.virtualAddr/PageSize].physicalPage * PageSize + (noffH.code.virtualAddr%PageSize)]), noffH.initData.size, noffH.initData.inFileAddr);
    
          // For HW3, it's needed, but not in HW1
          executable->ReadAt(&(kernel->machine->mainMemory[noffH.initData.virtualAddr]),noffH.initData.size, noffH.initData.inFileAddr);
      }
      /*-----------------------Homework for Memory Management------------------------*/
    
      delete executable;      // close file
      return TRUE;            // success   }   void AddrSpace::Execute(char *fileName)    {
      /*-----------------------Homework for Memory Management------------------------*/
      pageTable_is_load=FALSE;
      /*-----------------------Homework for Memory Management------------------------*/
      if (!Load(fileName))
      ...
      /*-----------------------Homework for Memory Management------------------------*/
      pageTable_is_load=TRUE;
      /*-----------------------Homework for Memory Management------------------------*/
      ...   }   void AddrSpace::SaveState()    {
      /*-----------------------Homework for Memory Management------------------------*/
      if(pageTable_is_load)
      {
          pageTable=kernel->machine->pageTable;
          numPages=kernel->machine->pageTableSize;
      }
      /*-----------------------Homework for Memory Management------------------------*/   }   ```
    
  • /code/userprog/addrspace.h Defined variable ID and pageTable_is_load
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
      class AddrSpace
      {
          public:
              ...
              /*-----------------------Homework for Memory Management------------------------*/
              int ID;
              /*-----------------------Homework for Memory Management------------------------*/
          private:
              ...
              static int NumFreePages;
              /*-----------------------Homework for Memory Management------------------------*/
              bool pageTable_is_load;
              /*-----------------------Homework for Memory Management------------------------*/
      };
    

    Now, the OS can implement that swap the data from main memory to virtual memory when main memory is full. For the following step, we have to implement page replacement algorithms, Least Recently Used (LRU).

  • /code/machine/translate.h

    We implement by a hardware counter LRU_counter in /code/machine/traslate.h. - by pai445

    1
    2
    3
    4
    5
    6
    7
    8
    9
      class TranslationEntry
      {
          public:
              ...
              /*-----------------------Homework for Memory Management------------------------*/
              int LRU_counter;    // counter for LRU
              int ID;             // page table ID
              /*-----------------------Homework for Memory Management------------------------*/
      };
    
  • /code/machine/translate.cc

    First, we check the valid-invalid bit of the page table. If is not valid, it means the page is not in the main memory. Hence, we need to load from the secondary storage. There are two cases that may happen. First, if there are some empty frame in the main memory, then we can just load the page into it. The other case is that the main memory is fulled. In this cases, we create two buffers and runs least recently used (LRU) algorithm to find the victim. The ReadSector and WriteSector are used to read/write the temporarily saved pages found by the LRU algorithm. The victim is pull out from the main memory, and then swapped by our page into the corresponding frame. The page table will be updated correspondingly in both cases. In our implementation, LRU will perform linear search on LRU_counter to find the least recently used page. - by pai445

    • In previous TA’s Project documentation, we used kernel->swap->WriteSector and kernel->swap->ReadSector to store the virtual memory from hard disk(swap is a object of SynchDisk) Therefore, in line <font color=#FF0000>34-35</font>, we used ReadSector to read pageTable[vpn].virtualPage and store into the buffer. Then use bcopy function to move byte sequence from srcbuffer to dest&mainMemory[j*PageSize] with size=PageSize only if the main memory is sufficient. <font color=#FF0000>Reference[1]</font>
    • In line <font color=#FF0000>63-66</font>. If the main memory is full, then we used LRU algorithm to find the least recently used memory and switch to disk and replace an desired data to main memory. That is, memory section was found by LRU algorithmbuffer1virtual memory and desired memory section stored in virtual memorybuffer2main memory
        ExceptionType Machine::Translate(int virtAddr, int* physAddr, int size, bool writing)
        {
        unsigned int pageFrame;
        /*-----------------------Homework for Memory Management------------------------*/
        int victim;///find the page victim
        unsigned int j;
        /*-----------------------Homework for Memory Management------------------------*/
        DEBUG(dbgAddr, "\tTranslate " << virtAddr << (writing ? " , write" : " , read"));
        ...
        if (tlb == NULL)
        {		// => page table => vpn is index into table
            if (vpn >= pageTableSize){...}
            else if (!pageTable[vpn].valid)
            {
                /*-----------------------Homework for Memory Management------------------------*/
                // For HW3, these two lines are no longer needed
                // DEBUG(dbgAddr, "Invalid virtual page # " << virtAddr);
                // return PageFaultException;
                kernel->stats->numPageFaults++; // page fault
                j=0;
                while(kernel->machine->UsedPhyPage[j]!=FALSE&&j<NumPhysPages){j++;}
                // load the page into the main memory if the main memory is not full  
                if(j<NumPhysPages)
                {
                    char *buffer; //temporary save page 
                    buffer = new char[PageSize];
                    kernel->machine->UsedPhyPage[j]=TRUE;
                    kernel->machine->PhyPageInfo[j]=pageTable[vpn].ID;
                    kernel->machine->main_tab[j]=&pageTable[vpn];
                    pageTable[vpn].physicalPage = j;
                    pageTable[vpn].valid = TRUE;
                    pageTable[vpn].LRU_counter++; // counter for LRU
      
                    kernel->SwapDisk->ReadSector(pageTable[vpn].virtualPage, buffer);
                    bcopy(buffer,&mainMemory[j*PageSize],PageSize);
                }
                // main memory is full, page replacement
                else
                {
                    char *buffer1;
                    char *buffer2;
                    buffer1 = new char[PageSize];
                    buffer2 = new char[PageSize];
                    //Random
                    victim = (rand()%32);
      
                    //LRU
                    int min = pageTable[0].LRU_counter;
                    victim=0;
                    for(int index=0;index<32;index++)
                    {
                        if(min > pageTable[index].LRU_counter)
                        {
                            min = pageTable[index].LRU_counter;
                            victim = index;
                        }
                    }
                    pageTable[victim].LRU_counter++;  
      
                    //printf("Number %d page is swapped out\n",victim);
      
                    // perform page replacement, write victim frame to disk, read desired frame to memory
                    bcopy(&mainMemory[victim*PageSize],buffer1,PageSize);
                    kernel->SwapDisk->ReadSector(pageTable[vpn].virtualPage, buffer2);
                    bcopy(buffer2,&mainMemory[victim*PageSize],PageSize);
                    kernel->SwapDisk->WriteSector(pageTable[vpn].virtualPage,buffer1);
      
                    main_tab[victim]->virtualPage=pageTable[vpn].virtualPage;
                    main_tab[victim]->valid=FALSE;
      
                    //save the page into the main memory
      
                    pageTable[vpn].valid = TRUE;
                    pageTable[vpn].physicalPage=victim;
                    kernel->machine->PhyPageInfo[victim]=pageTable[vpn].ID;
                    main_tab[victim]=&pageTable[vpn];
                    //printf("Page replacement finish\n");
                }
                /*-----------------------Homework for Memory Management------------------------*/
            }
            entry = &pageTable[vpn];
        }
        ...
        }   
      

Result

./nachos -e ../test/sort result of sort ./nachos -e ../test/matmult result of matmult ./nachos -e ../test/matmult -e ../test/sort result of matmult and sort

Reference