项目代码下载: github
(程序实现方法参考于 @Tony Poerio)
如有任何问题,欢迎提出指正~
一、LRU的计数器实现(counter implementation)
原理:为page table 中的每一个entry都添加一个“时间域” , 然后CPU添加一个时钟或者计数器。一个page每次被引用,时钟或计数器的值就会被 copy 到此 page 所在 entry 的“ 时间域 ”内,发生 page fault 时就将时间最小或者计数值最小的被替换。
def add_or_update_successful(self, vpn, read_or_write):
# first check if we have a hit
for elem in self.frame_list:
if elem.VPN == vpn:
# mark that we had a hit
self.hit = True
self.CPUcounter += 1 # 全局的CPU计时器每次操作+1
elem.in_use = True
elem.VPN = vpn
# if we're doing a write, need to set dirty bit
if read_or_write == 'W':
elem.dirty = True
# selected VPN was accessed
else:
elem.pcount = self.CPUcounter
# 把页面加入帧数组之中,并将其计数器更新为最新
# and return
return True
else:
self.hit = False
# try and add to an empty space, even though we have a page fault
if not self.add_after_page_fault(vpn, read_or_write):
# and if that returns false, we need to EVICT and try again
self.evict = True
self.evict_page()
return False
else:
return True
def add_after_page_fault(self, vpn, read_or_write):
for elem in self.frame_list:
if not elem.in_use:
elem.in_use = True
elem.VPN = vpn
# if we're doing a write, need to set dirty bit
if read_or_write == 'W':
elem.dirty = True
else:
# if we have a read, mark our reference
# 并将其计数器更新为最新
elem.pcount = self.CPUcounter
# and return
return True
# if we make it this far, then all items are in use, so return false
return False
def evict_page(self):
lowest_value = self.CPUcounter
lowest_value_vpn = 0
lowest_value_ppn = 0
for elem in self.frame_list:
# index by the key to get a value
# value = elem.last_reference
value = elem.pcount # 选出计时器最小的作为替换页
# find the lowest value overall, and get its PPN and VPN
if value < lowest_value:
lowest_value = value
lowest_value_vpn = elem.VPN
lowest_value_ppn = elem.PPN
# remove the loest value vpn
self.remove(lowest_value_ppn, lowest_value_vpn)
二、LRU的栈实现(stack implementation)
原理:采用栈的方式,栈的实现可以用指针链表。每当引用一个page,该 page 就从栈中删除,然后重新放到栈顶,这样栈底部的 page 就是最久未使用的,将作为替换页。
我们运用 frame_list 模拟栈的实现,0元素为栈底
def inPPN(self): # 排序编号,方便索引被击中的元素
# initalize our PPNs # 但是多次调用这个函数,好像增加了时间,失去了一定效率
counter = 0
for elem in self.frame_list:
elem.PPN = counter
counter += 1
def add_or_update_successful(self, vpn, read_or_write):
# first check if we have a hit
for elem in self.frame_list:
if elem.VPN == vpn:
# mark that we had a hit
self.hit = True
# add the page
# set values accordingly
elem.in_use = True
elem.VPN = vpn
# 这里尊重原作者的想法把write,read区别对待,虽然我觉得write也属于最近访问
# if we're doing a write, need to set dirty bit
if read_or_write == 'W':
elem.dirty = True
# if we have a READ
# selected VPN was accessed
else: #被访问,则从栈底移到栈顶
self.frame_list.pop(elem.PPN)
self.frame_list.append(elem)
self.inPPN()
# and return
return True
else:
self.hit = False
# try and add to an empty space, even though we have a page fault
if not self.add_after_page_fault(vpn, read_or_write):
# and if that returns false, we need to EVICT and try again
self.evict = True
# if the page is dirty, we need to do a disk write
if self.frame_list[0].dirty:
self.dirty = True
self.frame_list.pop(0) # 如果替换就移除栈底元素
self.numframe -= 1
return False
else:
return True
def add_after_page_fault(self, vpn, read_or_write):
if self.numframe < self.PAGE_TABLE.num_frames: # 帧满了
newframe = pg.Frame()
newframe.dirty = False
newframe.in_use = False
self.frame_list.append(newframe)
self.inPPN()
newframe.in_use = True
newframe.VPN = vpn
self.numframe += 1
if read_or_write == 'W':
newframe.dirty = True
# and return
return True
# if we make it this far, then all items are in use, so return false
elif self.numframe >= self.PAGE_TABLE.num_frames:
return False
三、Additional-Reference-Bits Algorithm
- Keep an 8-bit bytes for each page in main memory.
- At regular intervals, shifts the bits right 1 bit, shift the reference bit into the lower-order bit.
- Interpret these 8-bit bytes as unsigned integers, the page with lowest number is the LRU page.
给每个frame设置reference bit:
# 在 frame类中添加如下代码
# reference bit 的实现类似于移位寄存器, 8 bit, for ARB
bit_string = '{:0%db}' % 8
self.reference = bit_string.format(128) # initialize as '10000000'
每一次的移位算法:
但只是逻辑,最好在代码里添加
def shift(self, vpn):
# left_shift
for elem in self.frame_list:
if elem.in_use == True:
temp = elem.reference[:-1]
if elem.VPN == vpn:
elem.reference = '1' + temp
else:
elem.reference = '0' + temp
选出要替换的页:
def evict_page(self):
lowest_value = 128
lowest_value_vpn = 0
lowest_value_ppn = 0
for elem in self.frame_list:
# index by the key to get a value
value = int(elem.reference, 2) # 越最近使用的'1'在最高位,其数值越大
if value < lowest_value: # 所以选择出数值最小的页移除
lowest_value = value
lowest_value_vpn = elem.VPN
lowest_value_ppn = elem.PPN
# remove the lowest value vpn
self.remove(lowest_value_ppn, lowest_value_vpn)
主要实现:
def add_or_update_successful(self, vpn, read_or_write):
# first check if we have a hit
for elem in self.frame_list:
temp = elem.reference[:-1]
if elem.VPN == vpn:
elem.reference = '1' + temp
# mark that we had a hit
self.hit = True
# add the page
# set values accordingly
elem.in_use = True
elem.VPN = vpn
# if we're doing a write, need to set dirty bit
if read_or_write == 'W':
elem.dirty = True
# if we have a READ
# selected VPN was accessed
return True
else:
elem.reference = '0' + temp
self.hit = False
# try and add to an empty space, even though we have a page fault
if not self.add_after_page_fault(vpn, read_or_write):
# and if that returns false, we need to EVICT and try again
self.evict = True
self.evict_page()
return False
else:
return True
def add_after_page_fault(self, vpn, read_or_write):
for elem in self.frame_list:
if not elem.in_use:
elem.in_use = True
elem.VPN = vpn
bit_string = '{:0%db}' % 8
elem.reference = bit_string.format(128)
# if we're doing a write, need to set dirty bit
if read_or_write == 'W':
elem.dirty = True
# and return
return True
# if we make it this far, then all items are in use, so return false
return False
四、 Second chance Algorithm (clock+reference bit)
设置循环队列类 Circular queue ,并在类中设置 指针 pointer;
设置(使用位,修改位)为:referencebit 和 Dirty;
主要步骤实现: (其余步骤与前三种算法类似)
def find_victim(self):
for index in range(0,self.qsize):
elem = self.list[self.pointer]
# if we find a page which is unreferenced (recently) and clean, that's our victim
if elem.referencebit == False and elem.dirty == False: # (0, 0)
# return it's PPN, so we can index into it and remove it
return elem.PPN
elif elem.referencebit == False and elem.dirty == True: # (0, 1)
#skip, do nothing
continue
elif elem.referencebit == True and elem.dirty == False: # (1, 0)
elem.referencebit = False
elem.dirty = False
elif elem.referencebit == True and elem.dirty == True: # (1, 1)
elem.referencebit = False
# use modulus of queue size to achieve a circular queue
self.pointer = (self.pointer + 1) % self.qsize
assert self.pointer <= self.qsize
# if we get this far, no victim page was found,
# need to flush __dirty unreferenced pages__ to disk
# and then repeat
return None
def flush_dirty_and_unreferenced_pages(self):
# NOTE: need to account for a DISK WRITE in clock algorithm
# remove the dirty and unreferenced pages, count how many we removed
number_of_disk_writes = 0
for elem in self.list:
if elem.dirty == True and elem.referencebit == False:
elem.dirty = False
number_of_disk_writes += 1
return number_of_disk_writes