虚拟存储器采用的页面调度算法是“先进先出”(FIFO)算法吗?
页式虚拟存储器的页面置换算法一般有:
***置换算法(OPT),先进先出置换算法(FIFO),最近最久未使用置换算法(LRU),Clock置换算法,最少使用置换算法(LFU),页面缓存算法(PBA)等。
先进先出(FIFO)置换算法是最直观的置换算法,由于它可能是性能最差的算法,故实际应用极少。(摘录自汤的教材)
fifo算法是什么?
先进先出算法是最简单的分页替换算法,是指每次有新的分页需要调入时,会选择调入内存时间最久的分页换出。它简单,容易实现,但这种绝对的公平方式容易导致效率的降低。
最简单的分页替换算法就是先进先出算法,当每次有新的分页需要调入时,会选择调入内存时间最久的分页换出。
有两种实现的方法:***种是记录每个分页被调入到页框的时间,当每次需要换出分页时,会找到调入时间最早的一页,也就是在主存储器中存在最久的分页。另外一种方式就是利用FIFO队列来实现,当要进行分页替换时,就把队列最前端的分页换出,再把要调入的分页放到队列的末端。
一、实现机制
使用链表将所有在内存的页面按照进入时间的早晚链接起来,然后每次置换链表头上的页面就行了。新加进来的页面则挂在链表的末端。
二、特点
1、优点
简单,且容易实现。
2、缺点
这种绝对的公平方式容易导致效率的降低。例如,如果***加载进来的页面是经常被访问的页面,这样做很可能造成常被访问的页面替换到磁盘上,导致很快就需要再次发生缺页中断,从而降低效率。
电子产品
FIFO通常在电子电路中用于硬件和软件之间的缓冲和流控制。FIFO以其硬件形式主要由一组读写指针,存储和控制逻辑组成。
存储可以是静态随机存取存储器(SRAM),触发器,锁存器或任何其他合适的存储形式。对于非平凡大小的FIFO,通常使用双端口SRAM,其中一个端口专用于写入,另一端口专用于读取。
电子设备中实现的***个已知FIFO是1969年在飞兆半导体公司的Peter Alfke 。[4] Alfke后来担任Xilinx的董事。
1、同步性
同步FIFO是其中相同的时钟用于读取和写入的FIFO。异步FIFO使用不同的时钟进行读取和写入,它们可能会引入亚稳定性问题。异步FIFO的常见实现方式是对读和写指针使用格雷码(或任何单位距离码),以确保可靠的标志生成。
关于标志生成的另一条注释是,必须使用指针算法为异步FIFO实现生成标志。相反,在同步FIFO实现中,可以使用泄漏存储区方法或指针算法来生成标志。
2、状态标志
FIFO状态标志的示例包括:已满,为空,几乎已满和几乎为空。当读地址寄存器到达写地址寄存器时,FIFO为空。当写地址寄存器到达读地址寄存器时,FIFO已满。读写地址最初都位于***个存储器位置,并且FIFO队列为空。
在这两种情况下,读和写地址最终都是相等的。为了区分这两种情况,一种简单而强大的解决方案是为每个读取和写入地址添加一个额外的位,该地址在每次换行时都会反转。
以上内容参考 百度百科-先进先出算法
FIFO和LRU小结
一:FIFO算法
1.0,FIFO (First in First out) 先进先出(核心原则:***进来的,***淘汰); 其实在操作系统的设计理念中很多地方都是利用到了先进先出的思想就是因为这个原则简单切符合人们的惯性思维,具备公平性实现起来也简单,直接使用数据结构中的队列即可实现
1.1,在FIFO 中应该支持这些操作: 一,get(key),如果Cache中存在该key,则返回对应的value值,否则返回 -1; 二,set(key,value),如果Cache中存在该key,则重置value值,如果不存在则将该key插入到Cache中,若Cache已满则淘汰***进入Cache的数据
1.2,那么利用什么数据结构来实现呢?有这一种思路, 利用一个双向链表保存数据,当来了新数据之后便添加到链表末尾,如果Cache存满数据,则把链表头部数据删除,然后把次年数据添加到链表末尾,在访问数据的时候,如果在Cache中存在该数据的话,则返回对应的value值,否则返回 -1,如果想提高访问效率,可以利用hashmap来保存每个key在链表中的位置(参考下面拓展)
二:LRU算法
1.0,LRU (Least recently used) 最近最久未使用调度,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也更高"; 最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:
1,新数据插入到链表头部
2,每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
3,当链表满的时候,将链表尾部的数据丢弃
1.1,LRU的优缺点 1.命中率,当存在热点数据时,LRU的效率很好,但偶发性的,周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重 2,实现相对简单 3,命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部
2.0,LRU-K K代表最近使用的次数,因此LRU也可以认为是LRU-1,它主要是为了解决LRU算法"缓存污染"的问题,其核心思想是将"最近使用过1次"的判断标准扩展为"最近使用过K次"; 相比LRU要多维护一个队列,用于记录所有缓存数据被访问的历史,只有当数据的访问次数达到K次的时候,才将数据放入缓存.当需要淘汰数据时,LRU-k会淘汰第K次访问时间距离当前时间***的数据.详细实现如下:
2.1,一,数据***次被访问,加入到访问历史列表; 二,如果数据在访问历史列表里后达到K次访问,则按照一定(FIFO, LRU)淘汰; 三,当访问历史队列中的数据访问次数达到k次后,将数据l索引从历史队列删除,将数据移到缓存队列中,并缓存此数据,缓存队列重新按照时间排序; 四,缓存数据队列中被再次访问后,重新排序; 五,需要淘汰数据时,淘汰缓存队列中排在末尾的数据(即:淘汰倒数第K次访问离现在最久的数据)
2.2,LRU-K具有LRU的优点,同时能够避免LRU的缺点,实际应用中LRU-2是综合各种因素后***的选择,LRU-3或者更大的K值命中率会高,但适应性差,需要大量的数据访问才能将历史记录缓存或者清除掉
2.3优缺,LRU-K降低了"缓存污染"带来的问题,命中率比LRU要高,但LRU-K队列是一个优先级队列,算法复杂度和代价相对LRU较高,并且LRU需要记录那些被访问过,但是没有达到K次也就是还没有放入缓存的对象,因此b内存消耗会比LRU要多,当然如果数据量很大的时候,内存消耗会比较可观
3.0,Two queues (2Q) 算法类似于LRU-2,不同点在于2Q将LRU-2算法中的访问历史队列(历史队列,还没有缓存数据)改为一个FIFO缓存队列,即: 2Q算法有两个缓存队列,一个是FIFO队列,一个是LRU队列.
3.1,当数据***次访问时,2Q算法会将数据缓存在FIFO队列里面,当数据第二次被访问时,则将数据从FIFO队列移到LRU队列里面,两个队列各自按照自己的方法淘汰数据; 一,新访问的数据插入到FIFO队列, 二,如果数据在FIFO队列中一直没有被再次访问,则最终按照FOFO规则淘汰, 三,如果数据在FIFO队列中被再次访问,则将数据移到LRU队列头部, 四,如果数据在LRU队列再次被访问,则将数据移到LRU队列头部, 五,LRU队列淘汰末尾的数据
3.2,可能会感觉FIFO队列比LRU队列短,但并不代表这是算法的要求,实际应用中两者比例没有硬性要求
3.3,2Q算法命中率高于LRU,切需要两个队列,但两个队列本身都比较简单,代价是FIFO和LRU代价之和; 2Q算法和LRU-2算法命中率类似,内存消耗也比较接近,但对于最后的缓存数据来说,2Q减少一次从原始储存读取数据或者计算数据的操作
4.0,Multi Queue (MQ) 算法根据访问频率将数据划分为多个队列,不同的队列具有不同的访问优先级,其核心思想是:优先缓存访问次数多的数据
4.1,MQ算法将缓存划分为多个LRU队列,每个队列对应不同的访问优先级,访问优先级是根据访问次数计算出来的,详情: 一,新插入的数据放入Q0; 二,每个队列按照LRU管理数据; 三,当数据访问次数达到一定次数需要提升优先级时将数据从当前队列删除,加入到高一级的队列头部; 四,为了防止高优先级数据永远不被淘汰,每个队列淘汰数据时,将数据从缓存中删除,将数据加入Q-history头部; 五,需要淘汰数据时,从***一级队列开始按照LRU淘汰,每个队列淘汰数据时,将数据从缓存中删除,将数据索引加入Q-history头部; 六,如果数据在Q-history中被重新访问,则重新计算其优先级,移到目标队列的头部; 七,Q-history按照LRU淘汰数据的索引
4.2,MQ降低了"缓存污染"带来的问题,命中率比LRU高,但MQ需要维护多个队列,切需要维护每个数据的访问时间,复杂度比较高,并且MQ需要记录每个数据的访问时间,需要定时扫码所有队列,代价也比较高
4.3,虽然MQ的队列看起来数量比较多,但由于所有队列之和受限于缓存容量的大小,因此这里多个队列长度之和和一个LRU队列是一样的,因此队列扫码性能接近
小结: 命中率 LRU-2 MQ(2) 2Q LRU ; 复杂度 LRU-2 MQ(2) 2Q LRU ; 代价 LRU-2 MQ(2) 2Q LRU ; 需要注意的是,命中率并不是越高越好,实际中应该根据业务需求和对数据的访问情况进行分析选择,LRU虽然看起来命中率低一些,切存在"缓存污染"的问题,但其简单切代价小,实际中反而应用更多
拓展:基于 双链表的 LRU 实现: 一,传统意义的LRU算法是每一个Cache对象设置一个定时器,每次Cache命中则给定时器 +1,而Cache用完需要淘汰旧内容,放置新内容时就查看所有的计时器,并将使用的内容替换掉; 其弊端很明显,如果Cache的数量少,问题不大,但如果Cache的空间过大,达到10W或者100W以上,一旦需要淘汰,则需要遍历所有计时器,其性能与资源消耗巨大,效率也就非常的慢了; 二,双链表原理,将Cache的所有位置都用双链表连接起来,当一个位置被命中之后,就将通过调整链表的指向,将该位置调整到链表头的位置,新加入Cache直接加到链表头中,这样在多次进行Cache操作后,最近被命中的就会被向链表头方向移动,而没有命中的则向链表后部移动,链表尾则表示最近最少命中的Cache,当需要替换内容时我们只需要淘汰链表最后的部分即可!
如果错误或者建议,欢迎下方留言,谢谢!
FIFO算法(假定开始时先把1,2,3,4号页面装入内存)
FIFO:
页 4 1 2 5 1 2 3 4 5
内存423 413 412 512 no no 532 534 no
LRU:
页 4 1 2 5 1 2 3 4 5
内存423 413 412 512 no no 312 342 345
楼主 看一下这个
(缺页发生 也就是 需要进行 交换 初始 装入内存的 三个页 是不发生缺页的 所以 从4开始)
上面是 装入的 页面 下面是 装入后 内存的状态 (no代表不缺页)
我 也是才看过 三级的教程 大概算了一下
FIFO 是 先进 先出 , 也就是的 每次 总是 不 最早进来的 换出去 和 页面值 无关(此算法是基于内存块的 顺序, 最长未更新的内存块 , 先更新, 明白这意思吧, 可以对照 前面的数据看下)
LRU 是 更新 最长为使用的 页面, 也就是 这个算法 是 根据页面值来 交换的
也就是 新装入的 页面值 如果 在内存快里面 有 就会更新这个 页面的 某个标记状态(标记 其多久未使用, 其实就是个 变量, 很容易实现)
显然 一直到5 都是和FIFO算法 是一样的 ,
为什么呢, 因为 前几页 都是 缺页的 并没有 改变 标记变量, 所以 就 按照 先装入,则 距今未使用时间最长,则 先交换的原则啦
开始需要 1(5后面那个) 那么 内存 目前状态时 512 , 1是在内存中的 不发生缺页,】
所以 更新 标记变量(标明 1刚被使用过)
然后 需要 2 内存中依然 存在 则 更新 2的 标记变量, 则 现在内存中 任然是 512 但是 标记变量已经变了 2最新, 1次之 , 5最久 (最久未使用) 所以下次 交换 就先 换 5
内存 变为 321 现在 3最新, 2次之, 1最久 下次缺页 就 换 1
思路 就是 这样。
FIFO算法的解释
#include stdio.h
#include stdlib.h
#define mSIZE 3//分配三个内存页框
#define pSIZE 12//总共12个进程
static int memery[mSIZE] = {0};
static int process[pSIZE] = {1,2,3,4,1,2,5,1,2,3,4,5};//页面访问序列
void FIFO();
int main()
{
get();
printf("n(FIFO)tcountn");
FIFO();
system("PAUSE");
return 0;
}
get()
{
int w[12]={1,2,3,4,1,2,5,1,2,3,4,5}; //需要访问的资源序列
int i,n;
for(i=0;i12;i++) //输出序列
{
printf("%d ",w[i]);
}
}
void FIFO()
{
int time[mSIZE] = {0}; //分配的三个页框初始化为0
int i = 0, j = 0;
int m = -1, n = -1;
int max = -1,maxtime = 0;
int count = 0;
for(i = 0; ipSIZE; i++) //开始循环,在页框中寻找所需资源
{
for(j=0; jmSIZE; j++) //判断页框中是否满
{
if(memery[j] == 0) //寻找空页框,并且记录页号
{
m = j;
break;
}
}
for(j = 0; j mSIZE; j++)
{
if(memery[j] == process[i]) //判断页框中是否有进程所需访问的资源
{
n = j;
}
}
for(j = 0; j mSIZE;j++) //记录在页框中存放最长时间的资源,即***个进入的资源
{
if(time[j]maxtime)
{
maxtime = time[j]; //将存放最长时间资源的计数器的值赋给maxtime
max = j;
}
}
if(n == -1) //由于没有在页框中找到所需资源,并且也表已满,发生缺页中断。
{
if(m != -1)
{
memery[m] = process[i]; //没有替换的资源,则它对应的计数器加一
time[m] = 0;
for(j = 0;j = m; j++)
{
time[j]++;
}
m = -1;
}
else
{
memery[max] = process[i]; //发生缺页中断,从前面的标记中寻找***个进入页表的资源替换
time[max] = 0; //替换后原来的最长则清0,
for(j = 0;j mSIZE; j++)
{
time[j]++; //替换后,此资源对应的计数器加一
}
max = -1;
maxtime = 0;
count++;
}
}
else
{
memery[n] = process[i];
for(j = 0;j mSIZE; j++) //一次分配对所有在页表中的资源的计数器加一
{
time[j]++;
}
n = -1;
}
for(j = 0 ;j mSIZE; j++)
{
printf("%d ",memery[j]); //输出此次资源访问时页表中资源的情况。
}
printf("t%dn",count);
}
}
FIFO调度算法和LRU算法
FIFO:先进先出调度算法
LRU:最近最久未使用调度算法
两者都是缓存调度算法,经常用作内存的页面置换算法。
打一个比方,帮助你理解。
你有很多的书,比如说10000本。
由于你的书实在太多了,你只能放在地下室里面。
你看书的时候不会在地下室看书,而是在书房看书。
每次,你想看书都必须跑到地下室去找出来你想看的书,
然后抱回来放到书桌上,之后才开始看。
还有就是,有一些书你会反复的看,今天看了也许过几天又要看。
总之,你自己是不知道你哪天会需要看哪本书的。
你的老师每天下课的时候会给你布置一个书单,让你晚上回去去看哪本书。
(假设你老师让你看的书在你的地下室里面都有)
跑地下室当然是非常麻烦的,所以你希望你的经常看的那些书***放在书桌上。
但是你的书房的书桌同时只能摆放10本书(这个是假设的啊)。
那么,问题来了。
到底把哪些说留在书桌上***呢?
这里说的***,就是说你尽量少的跑地下室去找书。
为了解决这个问题,人们发明了很多的算法。
其中,比较常见的就是上面这两种:FIFO算法和LRU算法。
FIFO算法
很简单,我把书桌上的10本书按照放置时间先后堆放成一堆。
这里的放置时间,就是说这本书在我的书桌上放了几天了。
每次要看书的时候,我先在书桌上找,找到就直接可以读了。
读完之后放回原来的位置就可以,不打乱顺序。
如果书桌上面没有我要读的书,就去地下室找。
找来之后,我就把书桌上放的时间最长的那本(也就是
书堆里面最下面的那本书)放回地下室。
然后把我今天需要看的这本书放在书堆的最上面。
LRU算法
也不难,我把书桌上的10本书按照阅读时间先后堆放成一堆。
这里的阅读时间,就是说我最近一次读这本书是几天之前。
每次要看书的时候,我先在书桌上找,找到就直接可以读了。
读完之后放在书堆的最上面。
如果书桌上面没有我要读的书,就去地下室找。
找来之后,我就把书桌上最久没有阅读的那本
(也就是书堆里面最下面的那本书)放回地下室。
然后把我今天需要看的这本书放在书堆的最上面。
上面这个比方,相信你可以看明白吧。
这里的地下室对应内存,书桌对应缓存,书对应页面。
关于fifo算法和fifo算法和fcfs算法的区别的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。