Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
tobyatgithub committed Oct 31, 2022
2 parents 0a8b533 + b03f8f7 commit 840c302
Show file tree
Hide file tree
Showing 22 changed files with 1,344 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
.vs_code
*/.idea
*/cmake-build-*
ls-trace.txt
vpn.txt
result.txt
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"args": ["-s", "0", "-c"]
}
]
}
54 changes: 54 additions & 0 deletions Homework7/chap20/Chap20Simulator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Homework Simulation Chapter 20 Smaller Pages

## Question 1

> With a linear page table, you need a single register to locate the page table, assuming that hardware does the lookup upon a TLB miss. How many registers do you need to locate a two-level page table? A three-level table?
**Answer:**

Based on figure 20.3, I believe only the top level page directory base registor is required (PDBR).

## Question 2

> Use the simulator to perform translations given random seeds 0, 1, and 2, and check your answers using the -c flag. How many memory references are needed to perform each lookup?
**Answer:**

`python paging-multilevel-translate.py -s 0 -c`

Here I used a python debugger with config file to investigate the process (which is pretty much the same calculation as the textbook.)

As described in the function:

```python
def translate(self, pid, virtualAddr):
(valid, ptPtr, pdeAddr) = self.getPageDirEntry(pid, virtualAddr, True)
if valid == 1:
ptePage = ptPtr
(valid, pfn, pteAddr) = self.getPageTableEntry(virtualAddr, ptePage, True)
if valid == 1:
offset = (virtualAddr & self.OFFSET_MASK)
paddr = (pfn << self.pageBits) | offset
# print(' --> pfn: %02x offset: %x' % (pfn, offset))
return paddr
else:
return -2
return -1
```

here we first call `self.getPageDirEntry`, if valid then call the `self.getPageTableEntry`, and if valid, calculate the offset and actually page address in the end.

And the memory involved methods are:
```python
pageDir = self.pdbr[pid]
pde = self.memory[pdeAddr]
pte = self.memory[pteAddr]
```
so three times.

## Question 3

> Given your understanding of how cache memory works, how do you think memory references to the page table will behave in the cache? Will they lead to lots of cache hits (and thus fast accesses?) Or lots of misses (and thus slow accesses)?
**Answer:**
By the figure 20.3, the top level page directory lookup will work well with temporal locality cache because we will revisit each top-level PFN multiple times. And the page table level lookup will be a little bit harder to cache (i.e. probably more cache misses) and will suit spatial locality cache beter because each PFN is not likely to be revisited but revisit will happen to other PFNs that is close to each other in the memory.
272 changes: 272 additions & 0 deletions Homework7/chap20/paging-multilevel-translate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
#! /usr/bin/env python

from __future__ import print_function
import sys
from optparse import OptionParser
import random
import math

# to make Python2 and Python3 act the same -- how dumb
def random_seed(seed):
try:
random.seed(seed, version=1)
except:
random.seed(seed)
return

def convert(size):
length = len(size)
lastchar = size[length-1]
if (lastchar == 'k') or (lastchar == 'K'):
m = 1024
nsize = int(size[0:length-1]) * m
elif (lastchar == 'm') or (lastchar == 'M'):
m = 1024*1024
nsize = int(size[0:length-1]) * m
elif (lastchar == 'g') or (lastchar == 'G'):
m = 1024*1024*1024
nsize = int(size[0:length-1]) * m
else:
nsize = int(size)
return nsize

def roundup(size):
value = 1.0
while value < size:
value = value * 2.0
return value


class OS:
def __init__(self):
# 4k phys memory (128 pages)
self.pageSize = 32
self.physPages = 128
self.physMem = self.pageSize * self.physPages
self.vaPages = 1024
self.vaSize = self.pageSize * self.vaPages
self.pteSize = 1
self.pageBits = 5 # log of page size

# os tracks
self.usedPages = []
self.usedPagesCount = 0
self.maxPageCount = int(self.physMem / self.pageSize)

# no pages used (yet)
for i in range(0, self.maxPageCount):
self.usedPages.append(0)

# set contents of memory to 0, too
self.memory = []
for i in range(0, self.physMem):
self.memory.append(0)

# associative array of pdbr's (indexed by PID)
self.pdbr = {}

# mask is 11111 00000 00000 --> 0111 1100 0000 0000
self.PDE_MASK = 0x7c00
self.PDE_SHIFT = 10

# 00000 11111 00000 -> 000 0011 1110 0000
self.PTE_MASK = 0x03e0
self.PTE_SHIFT = 5

self.VPN_MASK = self.PDE_MASK | self.PTE_MASK
self.VPN_SHIFT = self.PTE_SHIFT

# grabs the last five bits of a virtual address
self.OFFSET_MASK = 0x1f

def findFree(self):
assert(self.usedPagesCount < self.maxPageCount)
look = int(random.random() * self.maxPageCount)
while self.usedPages[look] == 1:
look = int(random.random() * self.maxPageCount)
self.usedPagesCount = self.usedPagesCount + 1
self.usedPages[look] = 1
return look

def initPageDir(self, whichPage):
whichByte = whichPage << self.pageBits
for i in range(whichByte, whichByte + self.pageSize):
self.memory[i] = 0x7f

def initPageTablePage(self, whichPage):
self.initPageDir(whichPage)

def getPageTableEntry(self, virtualAddr, ptePage, printStuff):
pteBits = (virtualAddr & self.PTE_MASK) >> self.PTE_SHIFT
pteAddr = (ptePage << self.pageBits) | pteBits
pte = self.memory[pteAddr]
valid = (pte & 0x80) >> 7
pfn = (pte & 0x7f)
if printStuff == True:
print(' --> pte index:0x%x [decimal %d] pte contents:0x%x (valid %d, pfn 0x%02x [decimal %d])' % (pteBits, pteBits, pte, valid, pfn, pfn))
return (valid, pfn, pteAddr)

def getPageDirEntry(self, pid, virtualAddr, printStuff):
pageDir = self.pdbr[pid]
pdeBits = (virtualAddr & self.PDE_MASK) >> self.PDE_SHIFT
pdeAddr = (pageDir << self.pageBits) | pdeBits
pde = self.memory[pdeAddr]
valid = (pde & 0x80) >> 7
ptPtr = (pde & 0x7f)
if printStuff == True:
print(' --> pde index:0x%x [decimal %d] pde contents:0x%x (valid %d, pfn 0x%02x [decimal %d])' %
(pdeBits, pdeBits, pde, valid, ptPtr, ptPtr))
return (valid, ptPtr, pdeAddr)

def setPageTableEntry(self, pteAddr, physicalPage):
self.memory[pteAddr] = 0x80 | physicalPage

def setPageDirEntry(self, pdeAddr, physicalPage):
self.memory[pdeAddr] = 0x80 | physicalPage

def allocVirtualPage(self, pid, virtualPage, physicalPage):
# make it into a virtual address, as everything uses this (and not VPN)
virtualAddr = virtualPage << self.pageBits
(valid, ptPtr, pdeAddr) = self.getPageDirEntry(pid, virtualAddr, False)
if valid == 0:
# must allocate a page of the page table now, and have the PD point to it
assert(ptPtr == 127)
ptePage = self.findFree()
self.setPageDirEntry(pdeAddr, ptePage)
self.initPageTablePage(ptePage)
else:
# otherwise, just extract page number of page table page
ptePage = ptPtr
# now, look up page table entry too, and mark it valid and fill in translation
(valid, pfn, pteAddr) = self.getPageTableEntry(virtualAddr, ptePage, False)
assert(valid == 0)
assert(pfn == 127)
self.setPageTableEntry(pteAddr, physicalPage)

# -2 -> PTE fault, -1 means PDE fault
def translate(self, pid, virtualAddr):
(valid, ptPtr, pdeAddr) = self.getPageDirEntry(pid, virtualAddr, True)
if valid == 1:
ptePage = ptPtr
(valid, pfn, pteAddr) = self.getPageTableEntry(virtualAddr, ptePage, True)
if valid == 1:
offset = (virtualAddr & self.OFFSET_MASK)
paddr = (pfn << self.pageBits) | offset
# print(' --> pfn: %02x offset: %x' % (pfn, offset))
return paddr
else:
return -2
return -1

def fillPage(self, whichPage):
for j in range(0, self.pageSize):
self.memory[(whichPage * self.pageSize) + j] = int(random.random() * 31)

def procAlloc(self, pid, numPages):
# need a PDBR: find one somewhere in memory
pageDir = self.findFree()
# print('**ALLOCATE** page dir', pageDir)
self.pdbr[pid] = pageDir
self.initPageDir(pageDir)

used = {}
for vp in range(0, self.vaPages):
used[vp] = 0
allocatedVPs = []

for vp in range(0, numPages):
vp = int(random.random() * self.vaPages)
while used[vp] == 1:
vp = int(random.random() * self.vaPages)
assert(used[vp] == 0)
used[vp] = 1
allocatedVPs.append(vp)
pp = self.findFree()
# print('**ALLOCATE** page', pp)
# print(' trying to map vp:%08x to pp:%08x' % (vp, pp))
self.allocVirtualPage(pid, vp, pp)
self.fillPage(pp)
return allocatedVPs

def dumpPage(self, whichPage):
i = whichPage
for j in range(0, self.pageSize):
print(self.memory[(i * self.pageSize) + j], end='')
print('')

def memoryDump(self):
for i in range(0, int(self.physMem / self.pageSize)):
print('page %3d:' % i, end='')
for j in range(0, self.pageSize):
print('%02x' % self.memory[(i * self.pageSize) + j], end='')
print('')

def getPDBR(self, pid):
return self.pdbr[pid]

def getValue(self, addr):
return self.memory[addr]

# allocate some processes in memory
# allocate some multi-level page tables in memory
# make a bit of a mystery:
# can examine PDBR (PFN of current proc's page directory)
# can examine contents of any page
# fill pages with values too
# ask: when given
# LOAD VA, R1
# what will final value will be loaded into R1?

#
# main program
#
parser = OptionParser()
parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed')
parser.add_option('-a', '--allocated', default=64, help='number of virtual pages allocated',
action='store', type='int', dest='allocated')
parser.add_option('-n', '--addresses', default=10, help='number of virtual addresses to generate',
action='store', type='int', dest='num')
parser.add_option('-c', '--solve', help='compute answers for me', action='store_true', default=False, dest='solve')


(options, args) = parser.parse_args()

print('ARG seed', options.seed)
print('ARG allocated', options.allocated)
print('ARG num', options.num)
print('')

random_seed(options.seed)

# do the work now
os = OS()
used = os.procAlloc(1, options.allocated)

os.memoryDump()

print('\nPDBR:', os.getPDBR(1), ' (decimal) [This means the page directory is held in this page]\n')

for i in range(0, options.num):
if (random.random() * 100) > 50.0 or i >= len(used):
vaddr = int(random.random() * 1024 * 32)
else:
vaddr = (used[i] << 5) | int(random.random() * 32)
if options.solve == True:
print('Virtual Address 0x%04x:' % vaddr)
r = os.translate(1, vaddr)
if r > -1:
print(' --> Translates to Physical Address 0x%03x --> Value: 0x%02x' % (r, os.getValue(r)))
elif r == -1:
print(' --> Fault (page directory entry not valid)')
else:
print(' --> Fault (page table entry not valid)')
else:
print('Virtual Address %04x: Translates To What Physical Address (And Fetches what Value)? Or Fault?' % vaddr)

print('')

exit(0)




Loading

0 comments on commit 840c302

Please sign in to comment.