To
understand how multiple processes execute in a system, think about how
many students can work together by logging on to the same machine. It is
the responsibility of the OS to coordinate their execution.
Stock Nachos assumes only a single user program exists at any given time. Thus, mainMemory contains instructions for only one user program starting at location mainMemory . Thus, there is a one to one mapping between virtual and physical addresses - virtual address N maps to physical address N.
For the multiprogramming lab , you will need to add multiprogramming support to Nachos. This raises several new issues. First, now that we can have more than one process in memory, not all of them will begin from location mainMemory . Thus, we need a way to allocate memory to processes. Second, we need to coordinate access to the console device - only one process should be able to write to the console at any time. Third, understanding and implementing Exec and Join system calls becomes necessary.
Thus, at any time, there is a set of holes, of various sizes, scattered in memory. When a process arrives, the operating system has to choose which hole to allocate to the process. Three strategies are commonly used for this purpose:
Another problem that may occur with contiguous allocation is internal fragmentation . If a process is allocated more memory than it needs because there was no hole available that was an exact fit, there is some memory that the process has that it does not require. Internal fragmentation is thus internal to a process.
Every address generated by the CPU is divided into two parts - the page number and page offset . The page number is used to index into the page table to get the base address. This is combined with the page offset to determine the physical address. Note that with this scheme, there is no external fragmentation because any free frame can be allocated to a process that needs it. We may, however, still have internal fragmentation. If the memory requirements of a process are not exact multiples of the page size, the last frame allocated will not be completely full.
Each operating system has its own method of storing page tables. Most allocate a page table for each process. A pointer to the page table is stored with the other register values in the PCB. When the scheduler starts or switches to a process, it reloads the user registers and the correct page-table values from the page table for the process.
pageTable = new TranslationEntry[numPages]; for (i = 0; i < numPages; i++) { pageTable[i].virtualPage = i; // for now, virtual page # = phys page # pageTable[i].physicalPage = i; pageTable[i].valid = TRUE; pageTable[i].use = FALSE; pageTable[i].dirty = FALSE; pageTable[i].readOnly = FALSE; // if the code segment was entirely on // a separate page, we could set its // pages to be read-only }The above code fragment from the AddrSpace constructor, creates a page table for the process. Note that for each page the process occupies in memory, virtual page number is the same as the physical page. To implement multiprogramming, you must change this. Your memory management scheme must figure out what the physical page number for each virtual page is and fill it in.
To see how the MIPS simulator knows where to locate the instructions for a process, note the private variables pageTable and pageTableSize in the Machine class (machine.h ). When the scheduler switches between two processes, it calls a routine RestoreState(), defined in the AddrSpace class ( addrspace.cc). This routine initializes the pageTable and pageTableSize of the machine class to the page table for the process. Thus, the simulator's data structures are properly initialized to point to those of the process before it starts executing the instructions for that process.