您的位置:首页 > 编程语言 > Python开发

利用python实现OPT、FIFO、LRU、LFU、简单的和改进的CLOCK共六种页面置换算法,并对六种算法的过程和关系进行分析(操作系统课程设计)

2019-08-06 16:24 4551 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_44573410/article/details/98624560

特别声明:本博客属于原创内容,若需转载或引用,请均注明转载和加上相应的链接信息!!!!

设计目的

为了研究缺页率与物理块数、随机性之间的关系,同时将页面置换算法的置换过程进行动态呈现出来。在实际进程运行过程中,若其所要访问的页面不在内存,而需把她们呢调入内存,但内存已无空闲空间时,为了保证该进程能够正常运行,系统必须从内存中调出一页程序或数据送到磁盘的对换区,这时就需要合适的页面置换算法进行工作,置换算法的好坏将直接影响到系统的性能,不适当的算法可能会导致进程发生“抖动”。

设计内容

(1)请根据理论教材所授内容,采用自己熟悉的编程语言模拟实现OPT、FIFO、LRU、LFU、简单的和改进的CLOCK共六种页面置换算法。
(2)要求:(a) 各置换算法所请求的页面序列是随机产生的,而不是人为输入,在执行时应只需改变页面序列的大小就可以得到不同的页面序列,其中随机性通过一定的参数进行控制而且这些参数要便于调整。(b) 对每一算法,程序依次测试物理块数(内存容量)为2、3、4、5、6、7、8七种情况下的缺页率和置换率,并能自动统计分析出各算法缺页率与物理块、随机性之间的关系(要求程序最终能生成性能曲线图)。© 程序应能动态显示各算法的具体置换过程。

总体设计

开发环境

本设计使用的开发环境为Python3.6+window10,在eclipse上进行编译运行,运用了matplotlib、numpy、random三个库。

算法设计

本次设计中共利用Python实现了OPT、FIFO、LRU、LFU、简单和改进的CLOCK六种页面置换算法。
3.1 最佳置换算法(OPT)
介绍:
OPT的算法思想是选择以后永不使用的或是在(未来)最长时间内不再被访问的页面作为淘汰页面置换出去。其特点是理想化算法,具有最好的性能,可使缺页率最低,但在实际应用中难以确定理想的淘汰页面,因此算法难以实现。
其算法的实现过程为:当有新的访问页面要进入时,首先判断内存容量是否为满,如果不满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,如果不在,则将该页直接调入内存。如果内存已满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,如果不在,则判断在内存中的页号谁在未来最长时间不被访问(即判断各页号在未来时间里最早出来再次访问的序号最大的,假如不在被访问,则设序号为无限大)予以置换。
代码:

#作者:余创
def OPT(link,num1):                 #定义OPT算法函数  传入内存容量num1,工作的随机序列link
print("  使用OPT置换算法:")
links=[-1 for i in range (num1)]      #初始化内存序列
a1=0               # 初始化参数
max=0              # 初始化参数
give=0             # 初始化参数
error_OPT=0         # 初始化需要缺页次数
rate_OPT=0         #初始化缺页率
for i in range(len(link)):            #循环工作的随机序列
print(i,end=" ")
if (give<num1):
for j in range(num1):
if(links[j]==link[i]):    #判断是否命中
print("命中!")
break
else:
if (links[j]<0):
links[j]=link[i]   #将缺失页面加入内存
error_OPT=error_OPT+1  #缺页数加1
give=give+1
print(links)
break
else:
continue
elif(give==num1):       #判断内存是否满
if(link[i] in links):
print("命中!")
continue
else:
for j in range(num1):
if links[j] not in link[i+1 : len(link)]:  #判断访问页面是否在内存
a1=j
break
else:
for k in range(i+1,len(link)):    #循环从当前到工作序列末
if link[k]==links[j]:
if k>max:
max=k       #记录最远使用页面序号
a1=j
break
else:
break
for m in range(a1,num1-1):
links[m]=links[m+1]
links[num1-1]=link[i]
error_OPT=error_OPT+1
print(links)
rate_OPT=error_OPT/len(link)
rate_OPT_arr.append(rate_OPT)    #将缺页率加到rate_OPT_arr数组中

print("    当请求页面序列长度为{0},内存容量为{1},OPT缺页率为{2}" .format(len(link),num1,error_OPT/len(link)))
print("    当请求页面序列长度为{0},内存容量为{1},OPT置换率为{2}" .format(len(link),num1,(error_OPT-num1)/len(link)))

3.2先进先出页面置换算法(FIFO)
介绍:
FIFO的算法思想是总是淘汰最先进入内存的页面,即选择在内存驻留时间最长的页面予以淘汰 。其算法特点简单、易实现;貌似公平,实际上不公平,不切实际,有些经常被访问的页面可能先被淘汰,因此性能较差。
其算法实现过程:当有新的访问页面要进入时,首先判断内存容量是否为满,如果不满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,如果不在,则将该页直接调入内存。如果内存已满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,如果不在,则将最先进入内存的页面进行置换。
代码:(部分代码注释同OPT)

#作者:余创
def FIFO(link,num1):
print("  使用FIFO置换算法:")
links=[-1 for i in range (num1)]
give=0
error_FIFO=0
rate_FIFO=0
for i in range(len(link)):
print(i,end=" ")
if (give<num1):
for j in range(num1):
if(links[j]==link[i]):
print("命中!")
break
else:
if (links[j]<0):
links[j]=link[i]
print(links)
error_FIFO=error_FIFO+1
give=give+1
break
else:
continue
elif(give==num1):
if(link[i] in links):
print("命中!")
continue
else:
for k in range(num1-1):
links[k]=links[k+1]   #将队首的页面置换掉
links[num1-1]=link[i]     #将置换的页面放在队尾
error_FIFO=error_FIFO+1
print(links)
rate_FIFO=error_FIFO/len(link)
rate_FIFO_arr.append(rate_FIFO)

print("    当请求页面序列长度为{0},内存容量为{1},FIFO缺页率为{2}" .format(len(link),num1,error_FIFO/len(link)))
print("    当请求页面序列长度为{0},内存容量为{1},FIFO置换率为{2}" .format(len(link),num1,(error_FIFO-num1)/len(link)))

3.3 最近最久未使用(LRU)
介绍:
LRU的算法思想是选择最近一段时间最久未使用的页面予以淘汰。其算法的特点是:考虑了程序设计的局部性原理,有一定合理性,但页面的过去和未来走向无必然联系;需要跟踪记录每一页的使用情况,系统开销较大。
其算法实现过程:当有新的访问页面要进入时,首先判断内存容量是否为满,如果不满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,如果不在,则将该页直接调入内存。如果内存已满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,如果不在,则将选择最近一段时间最久未使用的页面予以淘汰(即将内存里的页号一一去判断其前最近在序列中的使用序号,序号最小者予以淘汰)。
代码:(部分代码注释同OPT)

#作者:余创
def LRU(link,num1):
print("  使用LRU置换算法:")
links=[-1 for i in range (num1)]
a1=0
give=0
error_LRU=0
rate_LRU=0
for i in range(len(link)):
min=5000000000
print(i,end=" ")
if (give<num1):
for j in range(num1):
if(links[j]==link[i]):
print("命中!")
break
else:
if (links[j]<0):
links[j]=link[i]
print(links)
error_LRU=error_LRU+1
give=give+1
break
else:
continue
elif(give==num1):
if(link[i] in links):
print("命中!")
continue
else:
for j in range(num1):
for k in range(i-1,-1,-1): #遍历工作序列当前位置到序列头
if link[k]==links[j]:
if k<min:
min=k   #记录内存中页号最早出现且序号最小予以置换
a1=j
break
else:
break
for m in range(a1,num1-1):
links[m]=links[m+1]
links[num1-1]=link[i]
error_LRU=error_LRU+1
print(links)
rate_LRU=error_LRU/len(link)
rate_LRU_arr.append(rate_LRU)
print("    当请求页面序列长度为{0},内存容量为{1},LRU缺页率为{2}" .format(len(link),num1,error_LRU/len(link)))
print("    当请求页面序列长度为{0},内存容量为{1},LRU置换率为{2}" .format(len(link),num1,(error_LRU-num1)/len(link)))

3.4 最少使用置换算法(LFU)
介绍:
LFU的算法思想是与LRU类似,设置一个移位寄存器记录页面访问情况,如要置换页面,选择∑Ri值最小的页面进行淘汰。其特点是:实现比较简单但并不能真正反映页面的使用情况。
其算法实现过程:当有新的访问页面要进入时,首先判断内存容量是否为满,如果不满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,如果不在,则将该页直接调入内存。如果内存已满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,如果不在,则将截止现在使用次数最少的页面进行置换,若相同则将最近的进行置换(即对内存中的每个页面依次判断他在使用序列里使用的次数,次数最小的予以淘汰)。
代码:部分代码注释同OPT)

作者:余创
def LFU(link,num1):
print("  使用LFU置换算法:")
links=[-1 for i in range (num1)]
a1=0
give=0
error_LFU=0
rate_LRU=0
for i in range(len(link)):
min=5000000000
min2=5000000000
print(i,end=" ")
if (give<num1):
for j in range(num1):
if(links[j]==link[i]):
print("命中!")
break
else:
if (links[j]<0):
links[j]=link[i]
print(links)
error_LFU
give=give+1
break
else:
continue
elif(give==num1):
if(link[i] in links):
print("命中!")
continue
else:
for j in range(num1):
a2=0
for k in range(0,i): #遍历工作序列当前位置到序列头
if link[k]==links[j]:
a2=a2+1

else:
continue

if min>a2:      #寻找使用次数最少的页面
min=a2
min2=k
a1=j
elif(min==a2):  #若相等则找序号最小的
if min2>k:
min2=k
a1=j

for m in range(a1,num1-1):
links[m]=links[m+1]
links[num1-1]=link[i]
error_LFU=error_LFU+1
print(links)
rate_LFU=error_LFU/len(link)
rate_LFU_arr.append(rate_LFU)
print("    当请求页面序列长度为{0},内存容量为{1},LFU缺页率为{2}" .format(len(link),num1,error_LFU/len(link)))
print("    当请求页面序列长度为{0},内存容量为{1},LFU置换率为{2}" .format(len(link),num1,(error_LFU-num1)/len(link)))

3.5 简单CLOCK置换算法
介绍:
简单CLOCK置换算法的算法思想是 (1)为每页设置一位访问位,再将内存中所有页面通过链接指针链成一个循环队列。(2)当某页被访问时,其访问位被置1,表示该页最近使用过。(3)置换算法在选择一页淘汰时,只需循环检查各页的访问位:如果为1,则将该访问位置0,暂不换出;如果为0,则将该页换出,算法终止。
其算法实现过程:当有新的访问页面要进入时,首先判断内存容量是否为满,如果不满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,如果不在,则将该页直接调入内存,同时将其访问位置1,同时指针指向修改位的下一位。如果内存已满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,同时将其访问位置1。如果不在,则从指针开始依次进行循环判断,若访问位为1则跳过,同时将其置0,若访问位为0,则进行置换,同时将其访问位置0。
代码:部分代码注释同OPT)

#作者:余创
def BASIC_CLOCK(link,num1):
print("  使用BASIC_CLOCK置换算法:")
links=[-1 for i in range (num1)]
give=0
link1=[0 for i in range (num1)]   #初始化化访问位
a1=0
a2=0
error_BASIC_CLOCK=0
rate_BASIC_CLOCK=0
for i in range(len(link)):
print(i,end=" ")
if (give<num1):
for j in range(num1):
if(links[j]==link[i]):
print("命中!")
break
else:
if (links[j]<0):
links[j]=link[i]
link1[j]=1  #序列对应访问位置1
print(links)
error_BASIC_CLOCK=error_BASIC_CLOCK+1
give=give+1
a2=a2+1
break
else:
continue
elif(give==num1):
for j in range(num1):
if(link[i] in links):
if (links[j]==link[i]):
link1[j]=1  #序列对应访问位置1
print("命中!")
break
else:
continue
else:
for k in range((a2 % num1),(a2 % num1)+num1+1): #循环遍历num1+1次
if link1[k%num1]==0:   #判断访问位是否为0
links[k%num1]=link[i]
error_BASIC_CLOCK=error_BASIC_CLOCK+1
print(links)
a2=k+1
link1[k%num1]=1
break
else:
link1[k%num1]=0
break
rate_BASIC_CLOCK=error_BASIC_CLOCK/len(link)
rate_BASIC_CLOCK_arr.append(rate_BASIC_CLOCK)
print("    当请求页面序列长度为{0},内存容量为{1},BASIC_CLOCK缺页率为{2}" .format(len(link),num1,error_BASIC_CLOCK/len(link)))
print("    当请求页面序列长度为{0},内存容量为{1},BASIC_CLOCK置换率为{2}" .format(len(link),num1,(error_BASIC_CLOCK-num1)/len(link)))

3.6 改进CLOCK置换算法
介绍:
改进CLOCK置换算法的算法思想是 改进型Clock算法每次选择的淘汰页面除了最近未被使用过,最好还未被修改过(使置换代价尽可能小)。每页设置一个访问位A和一个修改位M。算法步骤第1步(第1轮扫描):寻找第一类页面,将所遇到的第1个第一类页面作为淘汰页面,如果找不到,则转入第2步。第2步(第2轮扫描):寻找第二类页面,将所遇到的第1个第二类页面作为淘汰页面,如果找不到,则转入第3步。在扫描期间,将所有扫描过的页面访问位置0。第3步:将指针返回到开始的位置,转第1步。算法特点:减少了磁盘I/O次数;但可能要经过多轮扫描
表3-1 改进CLOCK置换算法页的种类
第一类页面 A=0 M=0 最佳淘汰页面
第二类页面 A=0 M=1 次佳淘汰页面
第三类页面 A=1 M=0 该页可能再被访问
第四类页面 A=1 M=1 该页可能再被访问

其算法实现过程:当有新的访问页面要进入时,首先判断内存容量是否为满,如果不满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,同时将访问位置1,修改位置1。如果不在,则将该页直接调入内存,同时将其访问位置1,修改位置0,同时指针指向修改位的下一位。如果内存已满,再判断所要访问的页面是否在内存中,如果在,则命中输出,不需要将该页调入内存,同时将其访问位置1,修改位置1。如果不在,则从指针开始依次进行循环判断,寻找A=0,M=0页面,将所遇到的第1此页面作为淘汰页面。如果找不到,则循环寻找A=0,M=1页面,在循环扫描期间,将所有扫描过的页面的访问位置0。若在一个循环内也没找到A=0,M=1页面,由于扫描过的页面的访问位置0,所以再进行寻找是否有A=0,M=0页面,若没有则必然有A=0,M=1页面进行置换。
代码:部分代码注释同OPT)

#作者:余创
def IMPROVE_CLOCK(link,num1):
print("  使用IMPROVE_CLOCK置换算法:")
links=[-1 for i in range (num1)]
give=0
link1=[0 for i in range (num1)] #初始化访问位序列
link2=[1 for i in range (num1)] #初始化修改位序列
a1=0
a2=0
error_IMPROVE_CLOCK=0
rate_IMPROVE_CLOCK=0
for i in range(len(link)):
print(i,end=" ")
if (give<num1):
for j in range(num1):
if(links[j]==link[i]):
print("命中!")
link1[j]=1 #访问位置1
link2[j]=1 #修改位置1
break
else:
if (links[j]<0):
links[j]=link[i]
error_IMPROVE_CLOCK=error_IMPROVE_CLOCK+1
link1[j]=1
link2[j]=0
print(links)
give=give+1
a2=a2+1
break
else:
continue
elif(give==num1):
for j in range(num1):
m=0
if(link[i] in links):
if (links[j]==link[i]):
link1[j]=1
link2[j]=1
print("命中!")
break
else:
continue
else:
for k in range((a2 % num1),(a2 % num1)+num1):
if (link1[k%num1]==0) & (link2[k%num1]==0):#判断是否为第一类页面
links[k%num1]=link[i]
error_IMPROVE_CLOCK=error_IMPROVE_CLOCK+1
print(links)
a2=k+1
link1[k%num1]=1
link2[k%num1]=0
m=m+1
break
else:
m=m+1
continue
if(m==num1):
for k in range((a2 % num1),(a2 % num1)+num1):
if (link1[k%num1]==0) & (link2[k%num1]==1):#判断是否为第二类页面
links[k%num1]=link[i]
error_IMPROVE_CLOCK=error_IMPROVE_CLOCK+1
print(links)
a2=k+1
link1[k%num1]=1
link2[k%num1]=0

break
else:
m=m+1
link1[k%num1]=0 #将所扫描到的循环页面访问位置0
if(m==2*num1):
for k in range((a2 % num1),(a2 % num1)+num1):
if (link1[k%num1]==0) & (link2[k%num1]==0):
links[k%num1]=link[i]
error_IMPROVE_CLOCK=error_IMPROVE_CLOCK+1
print(links)
a2=k+1
link1[k%num1]=1
link2[k%num1]=0

break
else:
m=m+1
continue
if(m==3*num1):
for k in range((a2 % num1),(a2 % num1)+num1):
if (link1[k%num1]==0) & (link2[k%num1]==1):
links[k%num1]=link[i]
error_IMPROVE_CLOCK=error_IMPROVE_CLOCK+1
print(links)
a2=k+1
link1[k%num1]=1
link2[k%num1]=0
break

break
rate_IMPROVE_CLOCK=error_IMPROVE_CLOCK/len(link)
rate_IMPROVE_CLOCK_arr.append(rate_IMPROVE_CLOCK)
print("    当请求页面序列长度为{0},内存容量为{1},IMPROVE_CLOCK缺页率为{2}" .format(len(link),num1,error_IMPROVE_CLOCK/len(link)))
print("    当请求页面序列长度为{0},内存容量为{1},IMPROVE_CLOCK置换率为{2}" .format(len(link),num1,(error_IMPROVE_CLOCK-num1)/len(link)))

观察缺页率和置换率

除了编写算法以外,还实现了对缺页率和置换率的观察(下图只是一部分)

显示置换过程

除了编写算法以外,还实现了显示置换过程(下图只是一部分)

缺页率与随机性之间关系

除了编写算法以外,还实现了对缺页率与随机性之间关系的探究


由图像观察可得虽然页面序列的随机性可以影响缺页率,但是影响的程度并不非常大,其相差在0.1以内。

缺页率与物理块数之间关系

除了编写算法以外,还实现了缺页率与物理块数之间关系的探究

由图像观察可得:缺页率随着物理块的增加明显减小。同时可以看出OPT页面置换算法的缺页率明显小于其他五种算法,FIFO、LRU、LFU、简单和改进的CLOCK的缺页率在物理块一样时相近。

由于篇幅原因,后面对于过程和关系研究代码在此不进行粘贴,我将所有的代码以及报告文档打包为一个压缩包,请点击
请求分页管理压缩包链接.

温馨提示:有任何问题,可以在留言区留言,我会回答大家的问题,觉得对你有帮助的,可以关注一下,我会定期更新博客,内容很广。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: