内存管理

实验目的

1、了解虚拟存储技术的特点,掌握虚拟存储请求页式存储管理中几种基本页面置换算法的基本思想和实现过程,并比较它们的效率。
2、了解程序设计技术和内存泄露的原因。

实验内容

1、模拟实现请求页式存储管理的几种基本页面置换算法
(1)最佳淘汰算法(OPT)
(2)先进先出的算法(FIFO)
(3)最近最久未使用算法(LRU))

实验原理

1、虚拟存储系统
UNIX中,为了提高内存利用率,提供了内外存进程对换机制;内存空间的分配和回收均以页为单位进行;一个进程只需将其一部分(段或页)调入内存便可运行;还支持请求调页的存储管理方式。
当进程在运行中需要访问某部分程序和数据时,发现其所在页面不在内存,就立即提出请求(向CPU发出缺中断),由系统将其所需页面调入内存。这种页面调入方式叫请求调页。
为实现请求调页,核心配置了四种数据结构:页表、页框号、访问位、修改位、有效位、保护位等。
2、页面置换算法
当CPU接收到缺页中断信号,中断处理程序先保存现场,分析中断原因,转入缺页中断处理程序。该程序通过查找页表,得到该页所在外存的物理块号。如果此时内存未满,能容纳新页,则启动磁盘I/O将所缺之页调入内存,然后修改页表。如果内存已满,则须按某种置换算法从内存中选出一页准备换出,是否重新写盘由页表的修改位决定,然后将缺页调入,修改页表。利用修改后的页表,去形成所要访问数据的物理地址,再去访问内存数据。整个页面的调入过程对用户是透明的。
(1)最佳淘汰算法(OPT):选择永不使用或在未来最长时间内不再被访问的页面予以替换。
(2)先进先出的算法(FIFO):选择在内存中驻留时间最久的页面予以替换。
(3)最近最久未使用算法(LRU):选择过去最长时间未被访问的页面予以替换。
3、首先用srand( )和rand( )函数定义和产生指令序列,然后将指令序列变换成相应的页地址流,并针对不同的算法计算出相应的命中率。
(1)通过随机数产生一个指令序列,共320条指令。指令的地址按下述原则生成:
A:50%的指令是顺序执行的
B:25%的指令是均匀分布在前地址部分
C:25%的指令是均匀分布在后地址部分
具体的实施方法是:
A:在[0,319]的指令地址之间随机选取一起点m
B:顺序执行一条指令,即执行地址为m+1的指令
C:在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m’
D:顺序执行一条指令,其地址为m’+1
E:在后地址[m’+2,319]中随机选取一条指令并执行
F:重复步骤A-E,直到320次指令
(2)将指令序列变换为页地址流
设:页面大小为1K;
用户内存容量4页到32页;
用户虚存容量为32K。
在用户虚存中,按每K存放10条指令排列虚存地址,即320条指令在虚存中的存放方式为:
第 0 条-第 9 条指令为第0页(对应虚存地址为[0,9])
第10条-第19条指令为第1页(对应虚存地址为[10,19])
………………………………
第310条-第319条指令为第31页(对应虚存地址为[310,319])
按以上方式,用户指令可组成32页。

实验步骤

1、画出每个页面置换算法流程图;
这里写图片描述
最佳淘汰算法(OPT算法):
这里写图片描述
先进先出算法(FIFO):
这里写图片描述
最近最久未使用算法(LRU):
这里写图片描述
2、对算法所用的数据结构进行说明;

宏变量:

#define INVALID -1:用来初始化,置页面所在主存区的帧号为-1.表示该页不在主存中
#define  total_instruction  320:用来表示指令流长
#define  total_vp  32 :用来表示虚页长
#define  clear_period  50:用来表示清周期
结构体:
typedef struct                      //用来表示页面结构
{
    int pn,                     //用来表示页面序号
        pfn,                        //用来表示页面所在内存区的帧号
        counter,                    //用来表示单位时间内访问次数
        time;                   //用来表示上次访问的时间
}pl_type;
pl_type pl[total_vp];               //用来表示页面结构数组
struct pfc_struct {                //用来描述页面控制结构
    int pn,                     //用来表示页面号
        pfn;                        //用来表示内存区页面的帧号
    struct pfc_struct *next;        //用来表示页面指针,用于维护内存缓冲区的链式结构
};
typedef struct pfc_struct pfc_type;  //用来表示主存区页面控制结构别名
pfc_type pfc[total_vp],          //用来表示主存区页面控制结构数组
*freepf_head,                        //用来表示主存区页面控制结构的空闲页面头指针
*busypf_head,                        //用来表示主存区页面控制结构的忙页面头指针
*busypf_tail;                        //用来表示主存区页面控制结构的忙页面尾指针
int diseffect;              //用来表示页错误计数器,初次把页面载入主存时也当做页错误
int a[total_instruction];            //用来表示随即指令流数组
int page[total_instruction];         //用来表示指令对应的页面号
int offset[total_instruction];       //用来表示指令所在页面中的偏移量

int  initialize(int);               //定义函数用来初始化页面结构数组和页面控制结构数组
int  OPT(int);                      //最佳置换算法实现
int  FIFO(int);                     //先进先出算法实现
int  LRU(int);                      //最近最久未使用算法实现

3、测试数据随机产生。不可手工输入;
实验过程采用种子函数进行随机数产生和赋值,数据都是随机产生的。
4、 编写程序并调试;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifndef _UNISTD_H
#define _UNISTD_H
#include <IO.H>
#include <PROCESS.H>
#include<iostream>
#endif
#define TRUE 1
#define FALSE 0
#define INVALID -1                  //置页面所在主存区的帧号为-1.表示该页不在主存中
#define  total_instruction  320     //指令流长
#define  total_vp  32               //虚页长
#define  clear_period  50           //清周期
using namespace std;
typedef struct                      //页面结构
{
    int pn,                         //页面序号
        pfn,                        //页面所在内存区的帧号
        counter,                    //单位时间内访问次数
        time;                       //上次访问的时间
}pl_type;
pl_type pl[total_vp];               //页面结构数组
struct pfc_struct {                  //页面控制结构
    int pn,                     //页面号
        pfn;                        //内存区页面的帧号
    struct pfc_struct *next;        //页面指针,用于维护内存缓冲区的链式结构
};
typedef struct pfc_struct pfc_type;  //主存区页面控制结构别名
pfc_type pfc[total_vp],              //主存区页面控制结构数组
*freepf_head,                        //主存区页面控制结构的空闲页面头指针
*busypf_head,                        //主存区页面控制结构的忙页面头指针
*busypf_tail;                        //主存区页面控制结构的忙页面尾指针
int diseffect;                       //页错误计数器,初次把页面载入主存时也当做页错误
int a[total_instruction];            //随即指令流数组
int page[total_instruction];         //指令对应的页面号
int offset[total_instruction];       //指令所在页面中的偏移量
int  initialize(int);               //初始化页面结构数组和页面控制结构数组
int  OPT(int);                      //最佳置换算法
int  FIFO(int);                     //先进先出算法
int  LRU(int);                      //最近最久未使用算法
int main()
{
    int s;      //随机数
    int i;
    srand((unsigned)time(NULL)); /*每次运行时进程号不同,用来作为初始化随机数队列的"种子"*/
    s = (int)((float)(total_instruction - 1)*(rand() / (RAND_MAX + 1.0)));
    printf("\n------------随机产生指令流------------\n");
    for (i = 0; i<total_instruction; i += 4) //产生指令队列
    {
        a[i] = s; //任选一指令访问点m
        a[i + 1] = a[i] + 1; //顺序执行一条指令
        a[i + 2] = (int)((float)a[i] * (rand() / (RAND_MAX + 1.0))); //执行前地址指令m'
        a[i + 3] = a[i + 2] + 1; //顺序执行一条指令
        printf("%6d%6d%6d%6d\n", a[i], a[i + 1], a[i + 2], a[i + 3]);
        s = (int)((float)((total_instruction - 1) - a[i + 2])*(rand() / (RAND_MAX + 1.0))) + a[i + 2];
    }
    printf("--------------------------------------\n");
    for (i = 0; i<total_instruction; i++) //将指令序列变换成页地址流
    {
        page[i] = a[i] / 10;
        offset[i] = a[i] % 10;
    }
    printf("\n--不同页面工作区各种替换策略的命中率表--\n");
    printf("Page\t FIFO\t LRU\t OPT\n");
    for (i = 4; i <= 32; i++)   //用户内存工作区从个页面到个页面
    {
        printf(" %2d \t", i);
        FIFO(i);
        LRU(i);
        OPT(i);
        printf("\n");
    }
    cin >> i;
    return 0;
}
//初始化页面结构数组和页面控制结构数组
//total_pf;  用户进程的内存页面数
int initialize(int total_pf)
{
    int i;
    diseffect = 0;
    for (i = 0; i<total_vp; i++)
    {
        pl[i].pn = i;
        pl[i].pfn = INVALID;          //置页面所在主存区的帧号为-1.表示该页不在主存中
        pl[i].counter = 0;          //置页面结构中的访问次数为
        pl[i].time = -1;                //置页面结构中的上次访问的时间为-1
    }
    for (i = 0; i<total_pf - 1; i++)
    {
        pfc[i].next = &pfc[i + 1];        //建立pfc[i-1]和pfc[i]之间的链接
        pfc[i].pfn = i;               //初始化主存区页面的帧号
    }
    pfc[total_pf - 1].next = NULL;
    pfc[total_pf - 1].pfn = total_pf - 1;
    freepf_head = &pfc[0];          //主存区页面控制结构的空闲页面头指针指向pfc[0]
    return 0;
}
//最近最久未使用算法
//int total_pf;       用户进程的内存页面数
int LRU(int total_pf)
{
    int MinT;                   //最小的访问时间,即很久没被访问过
    int MinPn;                  //拥有最小的访问时间的页的页号
    int i, j;
    int CurrentTime;                            //系统当前时间
    initialize(total_pf);                       //初始化页面结构数组和页面控制结构数组
    CurrentTime = 0;
    diseffect = 0;
    for (i = 0; i<total_instruction; i++)
    {
        if (pl[page[i]].pfn == INVALID)             //页面失效
        {
            diseffect++;                            //页错误次数加
            if (freepf_head == NULL)               //无空闲页面
            {
                MinT = 100000;
                for (j = 0; j<total_vp; j++) {            //找出time的最小值,表明该页很久没被访问过
                    if (MinT>pl[j].time&&pl[j].pfn != INVALID)
                    {
                        MinT = pl[j].time;
                        MinPn = j;
                    }
                }
                freepf_head = &pfc[pl[MinPn].pfn];   //最久没被访问过的页被释放
                pl[MinPn].pfn = INVALID;             //最久没被访问过的页被换出主存
                pl[MinPn].time = -1;                //最久没被访问过的页的访问时间置为无效
                freepf_head->next = NULL;
            }
            pl[page[i]].pfn = freepf_head->pfn;      //有空闲页面,把相应的页面换入主存,并把pfn改为相应的帧号
            pl[page[i]].time = CurrentTime;         //令访问时间为当前系统时间
            freepf_head = freepf_head->next;        //减少一个空闲页面
        }
        else
            pl[page[i]].time = CurrentTime;        //命中则刷新该单元的访问时间
        CurrentTime++;                           //系统当前时间加
    }
    printf("%6.3f\t", 1 - (float)diseffect / 320);
    return 0;
}
//最佳置换算法
//int total_pf;       用户进程的内存页面数
int OPT(int total_pf)
{
    int i, j;
    int MaxD;                       //将来最近一次访问的距离的最大值(以时间单元度量)
    int MaxPn;                      //将来最近一次访问的距离的最大值的页号
    int dis;                        //距离计数器
    int dist[total_vp];             //距离数组,保存距离上一次访问的时间差距个数
    initialize(total_pf);               //初始化页面结构数组和页面控制结构数组
    diseffect = 0;
    for (i = 0; i<total_instruction; i++)
    {
        if (pl[page[i]].pfn == INVALID)     //页面失效
        {
            diseffect++;                    //页错误次数加
            if (freepf_head == NULL)            //无空闲页面
            {
                for (j = 0; j<total_vp; j++)
                {
                    if (pl[j].pfn != INVALID)   //如果该页在主存中
                        dist[j] = 100000;       // 该页关联的距离值改为最大值
                    else
                        dist[j] = 0;            //如果不在该页主存中,该页关联的距离值改为
                }
                dis = 1;                        //初始距离值为
                for (j = i + 1; j<total_instruction; j++)  //从要替换的指令的下一条算起,
                {
                    if (pl[page[j]].pfn != INVALID &&pl[page[j]].counter == 0)  //如果该页在主存中,并且是将要最近访问的页
                                                                                //if(pl[page[j]].pfn!=INVALID && dist[page[j]]==100000)  //此条语句原理与上相同
                    {
                        dist[page[j]] = dis;            //距离值改为dis
                        pl[page[j]].counter = 1;        //使访问次数标志加,区别第一次访问和第二次访问
                    }
                    dis++;
                }
                MaxD = -1;
                for (j = 0; j<total_vp; j++)
                {
                    pl[j].counter = 0;              //重置访问次数为
                    if (MaxD<dist[j])               //查找将来最近一次访问的距离的最大值及其序号
                    {
                        MaxD = dist[j];
                        MaxPn = j;
                    }
                }
                freepf_head = &pfc[pl[MaxPn].pfn];  //替换将来一段时间最久访问的页
                freepf_head->next = NULL;
                pl[MaxPn].pfn = INVALID;
            }
            pl[page[i]].pfn = freepf_head->pfn;    //把当前页换入主存中,并且把当前页的pfn改为换入页的帧号,
            freepf_head = freepf_head->next;         //减少一个空闲页面
        }//if
    }//for
    printf("%6.3f\t", 1 - (float)diseffect / 320);
    return 0;
}
//先进先出算法版本
//int total_pf;       用户进程的内存页面数
//实现细节由CLOCK算法退化而来,与FIFO同效果
int FIFO(int total_pf)
{
    int i;
    int use[total_vp];
    int swap = 0;
    initialize(total_pf);
    pfc_type *pnext, *head;
    pnext = freepf_head;
    head = freepf_head;
    for (i = 0; i<total_vp; i++) { use[i] = 0; }
    diseffect = 0;
    for (i = 0; i<total_instruction; i++)
    {
        if (pl[page[i]].pfn == INVALID)         //页面失效,不在主存中
        {
            diseffect++;
            if (freepf_head == NULL)               //无空闲页面
            {
                while (use[pnext->pfn] == 1)
                {
                    use[pnext->pfn] = 0;
                    pnext = pnext->next;
                    if (pnext == NULL) pnext = head;
                }
                //换出被替换的页
                pl[pnext->pn].pfn = INVALID;
                swap = 1;
            }
            if (use[pnext->pfn] == 0) {  //如果使用位为,则换入相应的页
                pl[page[i]].pfn = pnext->pfn;     //页面结构中要标记帧号
                pnext->pn = page[i];              //页面控制结构中要标记页号
                use[pnext->pfn] = 1;                //重置使用位为
                pnext = pnext->next;
                if (pnext == NULL) pnext = head;
                if (swap == 0) { freepf_head = freepf_head->next; }
            }
        }
    }
    printf("%6.3f\t", 1 - (float)diseffect / 320);
    return 0;
}

5、 多次测试程序,截屏输出实验结果;
测试数据一:
这里写图片描述
这里写图片描述
测试数据二:
这里写图片描述
这里写图片描述
测试数据三:
这里写图片描述
这里写图片描述
6、 根据实验结果与理论课讲述的原理进行实验分析。
理论上,四种替换算法的命中率由高到底排列应该是OPT>LRU>FIFO。实际上,从实验数据观测得到,LRU和FIFO不相上下,有高有低,而且相差不大。OPT则较为明显地在命中率上比其他两种算法要高。
效果不明显的原因:
推测与指令流的产生方式有关系。因为指令流的产生方式要能体现局部性原理,本实验采用的是随机数来产生指令流。这样的指令流设计方式能否最佳地体现局部性原理,这还有待验证。
同时,估计和指令数量有关系。因为320条指令太少了,通常一个稍大点的程序都几千行指令了。
而且由于随即数产生具有一定的波动性,该命中率的计算也有一定的波动性。所以会有局部的实验数据与理论不符。改进方法是多次实验取平均值,这样可以减小波动,让实验数据更加平滑。本次试验共进行三次实验,实验结果确实有些许差异。
唯一显著的是OPT算法的命中率与其他3个调度算法保持了比较大的差距。例如在page=30时,OPT算法就能达到0.9的命中率了。
到后期,由于page越来越大,因此越来越容易命中,因此各替换算法的命中率差距变小了。这由最后几行命中率相似可以看出。

实验数据及源代码

//本实验源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifndef _UNISTD_H
#define _UNISTD_H
#include <IO.H>
#include <PROCESS.H>
#include<iostream>
#endif
#define TRUE 1
#define FALSE 0
#define INVALID -1                  //置页面所在主存区的帧号为-1.表示该页不在主存中
#define  total_instruction  320     //指令流长
#define  total_vp  32               //虚页长
#define  clear_period  50           //清周期
using namespace std;
typedef struct                      //页面结构
{
    int pn,                         //页面序号
        pfn,                        //页面所在内存区的帧号
        counter,                    //单位时间内访问次数
        time;                       //上次访问的时间
}pl_type;
pl_type pl[total_vp];               //页面结构数组
struct pfc_struct {                  //页面控制结构
    int pn,                     //页面号
        pfn;                        //内存区页面的帧号
    struct pfc_struct *next;        //页面指针,用于维护内存缓冲区的链式结构
};
typedef struct pfc_struct pfc_type;  //主存区页面控制结构别名
pfc_type pfc[total_vp],              //主存区页面控制结构数组
*freepf_head,                        //主存区页面控制结构的空闲页面头指针
*busypf_head,                        //主存区页面控制结构的忙页面头指针
*busypf_tail;                        //主存区页面控制结构的忙页面尾指针
int diseffect;                       //页错误计数器,初次把页面载入主存时也当做页错误
int a[total_instruction];            //随即指令流数组
int page[total_instruction];         //指令对应的页面号
int offset[total_instruction];       //指令所在页面中的偏移量
int  initialize(int);               //初始化页面结构数组和页面控制结构数组
int  OPT(int);                      //最佳置换算法
int  FIFO(int);                     //先进先出算法
int  LRU(int);                      //最近最久未使用算法
int main()
{
    int s;      //随机数
    int i;
    srand((unsigned)time(NULL)); /*每次运行时进程号不同,用来作为初始化随机数队列的"种子"*/
    s = (int)((float)(total_instruction - 1)*(rand() / (RAND_MAX + 1.0)));
    printf("\n------------随机产生指令流------------\n");
    for (i = 0; i<total_instruction; i += 4) //产生指令队列
    {
        a[i] = s; //任选一指令访问点m
        a[i + 1] = a[i] + 1; //顺序执行一条指令
        a[i + 2] = (int)((float)a[i] * (rand() / (RAND_MAX + 1.0))); //执行前地址指令m'
        a[i + 3] = a[i + 2] + 1; //顺序执行一条指令
        printf("%6d%6d%6d%6d\n", a[i], a[i + 1], a[i + 2], a[i + 3]);
        s = (int)((float)((total_instruction - 1) - a[i + 2])*(rand() / (RAND_MAX + 1.0))) + a[i + 2];
    }
    printf("--------------------------------------\n");
    for (i = 0; i<total_instruction; i++) //将指令序列变换成页地址流
    {
        page[i] = a[i] / 10;
        offset[i] = a[i] % 10;
    }
    printf("\n--不同页面工作区各种替换策略的命中率表--\n");
    printf("Page\t FIFO\t LRU\t OPT\n");
    for (i = 4; i <= 32; i++)   //用户内存工作区从个页面到个页面
    {
        printf(" %2d \t", i);
        FIFO(i);
        LRU(i);
        OPT(i);
        printf("\n");
    }
    cin >> i;
    return 0;
}
//初始化页面结构数组和页面控制结构数组
//total_pf;  用户进程的内存页面数
int initialize(int total_pf)
{
    int i;
    diseffect = 0;
    for (i = 0; i<total_vp; i++)
    {
        pl[i].pn = i;
        pl[i].pfn = INVALID;          //置页面所在主存区的帧号为-1.表示该页不在主存中
        pl[i].counter = 0;          //置页面结构中的访问次数为
        pl[i].time = -1;                //置页面结构中的上次访问的时间为-1
    }
    for (i = 0; i<total_pf - 1; i++)
    {
        pfc[i].next = &pfc[i + 1];        //建立pfc[i-1]和pfc[i]之间的链接
        pfc[i].pfn = i;               //初始化主存区页面的帧号
    }
    pfc[total_pf - 1].next = NULL;
    pfc[total_pf - 1].pfn = total_pf - 1;
    freepf_head = &pfc[0];          //主存区页面控制结构的空闲页面头指针指向pfc[0]
    return 0;
}
//最近最久未使用算法
//int total_pf;       用户进程的内存页面数
int LRU(int total_pf)
{
    int MinT;                   //最小的访问时间,即很久没被访问过
    int MinPn;                  //拥有最小的访问时间的页的页号
    int i, j;
    int CurrentTime;                            //系统当前时间
    initialize(total_pf);                       //初始化页面结构数组和页面控制结构数组
    CurrentTime = 0;
    diseffect = 0;
    for (i = 0; i<total_instruction; i++)
    {
        if (pl[page[i]].pfn == INVALID)             //页面失效
        {
            diseffect++;                            //页错误次数加
            if (freepf_head == NULL)               //无空闲页面
            {
                MinT = 100000;
                for (j = 0; j<total_vp; j++) {            //找出time的最小值,表明该页很久没被访问过
                    if (MinT>pl[j].time&&pl[j].pfn != INVALID)
                    {
                        MinT = pl[j].time;
                        MinPn = j;
                    }
                }
                freepf_head = &pfc[pl[MinPn].pfn];   //最久没被访问过的页被释放
                pl[MinPn].pfn = INVALID;             //最久没被访问过的页被换出主存
                pl[MinPn].time = -1;                //最久没被访问过的页的访问时间置为无效
                freepf_head->next = NULL;
            }
            pl[page[i]].pfn = freepf_head->pfn;      //有空闲页面,把相应的页面换入主存,并把pfn改为相应的帧号
            pl[page[i]].time = CurrentTime;         //令访问时间为当前系统时间
            freepf_head = freepf_head->next;        //减少一个空闲页面
        }
        else
            pl[page[i]].time = CurrentTime;        //命中则刷新该单元的访问时间
        CurrentTime++;                           //系统当前时间加
    }
    printf("%6.3f\t", 1 - (float)diseffect / 320);
    return 0;
}
//最佳置换算法
//int total_pf;       用户进程的内存页面数
int OPT(int total_pf)
{
    int i, j;
    int MaxD;                       //将来最近一次访问的距离的最大值(以时间单元度量)
    int MaxPn;                      //将来最近一次访问的距离的最大值的页号
    int dis;                        //距离计数器
    int dist[total_vp];             //距离数组,保存距离上一次访问的时间差距个数
    initialize(total_pf);               //初始化页面结构数组和页面控制结构数组
    diseffect = 0;
    for (i = 0; i<total_instruction; i++)
    {
        if (pl[page[i]].pfn == INVALID)     //页面失效
        {
            diseffect++;                    //页错误次数加
            if (freepf_head == NULL)            //无空闲页面
            {
                for (j = 0; j<total_vp; j++)
                {
                    if (pl[j].pfn != INVALID)   //如果该页在主存中
                        dist[j] = 100000;       // 该页关联的距离值改为最大值
                    else
                        dist[j] = 0;            //如果不在该页主存中,该页关联的距离值改为
                }
                dis = 1;                        //初始距离值为
                for (j = i + 1; j<total_instruction; j++)  //从要替换的指令的下一条算起,
                {
                    if (pl[page[j]].pfn != INVALID &&pl[page[j]].counter == 0)  //如果该页在主存中,并且是将要最近访问的页
                                                                                //if(pl[page[j]].pfn!=INVALID && dist[page[j]]==100000)  //此条语句原理与上相同
                    {
                        dist[page[j]] = dis;            //距离值改为dis
                        pl[page[j]].counter = 1;        //使访问次数标志加,区别第一次访问和第二次访问
                    }
                    dis++;
                }
                MaxD = -1;
                for (j = 0; j<total_vp; j++)
                {
                    pl[j].counter = 0;              //重置访问次数为
                    if (MaxD<dist[j])               //查找将来最近一次访问的距离的最大值及其序号
                    {
                        MaxD = dist[j];
                        MaxPn = j;
                    }
                }
                freepf_head = &pfc[pl[MaxPn].pfn];  //替换将来一段时间最久访问的页
                freepf_head->next = NULL;
                pl[MaxPn].pfn = INVALID;
            }
            pl[page[i]].pfn = freepf_head->pfn;    //把当前页换入主存中,并且把当前页的pfn改为换入页的帧号,
            freepf_head = freepf_head->next;         //减少一个空闲页面
        }//if
    }//for
    printf("%6.3f\t", 1 - (float)diseffect / 320);
    return 0;
}
//先进先出算法版本
//int total_pf;       用户进程的内存页面数
//实现细节由CLOCK算法退化而来,与FIFO同效果
int FIFO(int total_pf)
{
    int i;
    int use[total_vp];
    int swap = 0;
    initialize(total_pf);
    pfc_type *pnext, *head;
    pnext = freepf_head;
    head = freepf_head;
    for (i = 0; i<total_vp; i++) { use[i] = 0; }
    diseffect = 0;
    for (i = 0; i<total_instruction; i++)
    {
        if (pl[page[i]].pfn == INVALID)         //页面失效,不在主存中
        {
            diseffect++;
            if (freepf_head == NULL)               //无空闲页面
            {
                while (use[pnext->pfn] == 1)
                {
                    use[pnext->pfn] = 0;
                    pnext = pnext->next;
                    if (pnext == NULL) pnext = head;
                }
                //换出被替换的页
                pl[pnext->pn].pfn = INVALID;
                swap = 1;
            }
            if (use[pnext->pfn] == 0) {  //如果使用位为,则换入相应的页
                pl[page[i]].pfn = pnext->pfn;     //页面结构中要标记帧号
                pnext->pn = page[i];              //页面控制结构中要标记页号
                use[pnext->pfn] = 1;                //重置使用位为
                pnext = pnext->next;
                if (pnext == NULL) pnext = head;
                if (swap == 0) { freepf_head = freepf_head->next; }
            }
        }
    }
    printf("%6.3f\t", 1 - (float)diseffect / 320);
    return 0;
}

思考题

1、从几种算法的命中率看,哪个算法最高?哪个算法最低?对每个页面的执行结果进行分析。
理论上,四种替换算法的命中率由高到底排列应该是OPT>LRU>FIFO。实际上,从实验数据观测得到,LRU和FIFO不相上下,有高有低,而且相差不大。OPT则较为明显地在命中率上比其他两种算法要高。
效果不明显的原因:
推测与指令流的产生方式有关系。因为指令流的产生方式要能体现局部性原理,本实验采用的是随机数来产生指令流。这样的指令流设计方式能否最佳地体现局部性原理,这还有待验证。
同时,估计和指令数量有关系。因为320条指令太少了,通常一个稍大点的程序都几千行指令了。
而且由于随即数产生具有一定的波动性,该命中率的计算也有一定的波动性。所以会有局部的实验数据与理论不符。改进方法是多次实验取平均值,这样可以减小波动,让实验数据更加平滑。本次试验共进行三次实验,实验结果确实有些许差异。
唯一显著的是OPT算法的命中率与其他3个调度算法保持了比较大的差距。例如在page=30时,OPT算法就能达到0.9的命中率了。
到后期,由于page越来越大,因此越来越容易命中,因此各替换算法的命中率差距变小了。这由最后几行命中率相似可以看出。
2、OPT算法在执行过程中可能会发生错误,为什么?
OPT算法本身实现就不现实,它只是一种理想算法,它是假设将来主存中的页面调度情况与过去一段时间内主存中的调度情况是相同的,这种假设并不总是正确的,因此在执行过程中会出现错误。

实验结果分析

根据三次实验结果与理论课讲述的原理进行实验分析:
理论上,四种替换算法的命中率由高到底排列应该是OPT>LRU>FIFO。实际上,从实验数据观测得到,LRU和FIFO不相上下,有高有低,而且相差不大。OPT则较为明显地在命中率上比其他两种算法要高。
效果不明显的原因:
推测与指令流的产生方式有关系。因为指令流的产生方式要能体现局部性原理,本实验采用的是随机数来产生指令流。这样的指令流设计方式能否最佳地体现局部性原理,这还有待验证。
同时,估计和指令数量有关系。因为320条指令太少了,通常一个稍大点的程序都几千行指令了。
而且由于随即数产生具有一定的波动性,该命中率的计算也有一定的波动性。所以会有局部的实验数据与理论不符。改进方法是多次实验取平均值,这样可以减小波动,让实验数据更加平滑。本次试验共进行三次实验,实验结果确实有些许差异。
唯一显著的是OPT算法的命中率与其他3个调度算法保持了比较大的差距。例如在page=30时,OPT算法就能达到0.9的命中率了。
到后期,由于page越来越大,因此越来越容易命中,因此各替换算法的命中率差距变小了。这由最后几行命中率相似可以看出。

(1)通过随机数产生一个指令序列,共320条指令。指令的地址按下述原则生成: ①、 50%的指令是顺序执行的; ②、 25%的指令是均匀分布在前地址部分; ③、 25%的指令是均匀分布在后地址部分。 具体的实施方法是: ① 在[0,319]的指令地址之间随机选取一起点m; ② 顺序 执行一条指令,即执行地址为m+1的指令; ③ 在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m’; ④ 顺序执行一条指令,其地址为m’+1; ⑤ 在后地址[m’+2,319]中随机选取一条指令并执行; ⑥ 重复上述步骤,直至执行320次指令。 (2) 将指令序列变换成页地址流 设:①页面大小为1K; ②用户内存容量为4页到32页; ③用户虚存容量为32K; 在用户虚存中,按每K存放10条指令排列虚存地址,即320条指令在虚存中的存放方式为: 第0条~第9条指令为第0页(对应的虚存地址为[0,9]); 第10条~第19条指令为第1页(对应的虚存地址为[10,19]); . 第310条~第319条指令为第31页(对应的虚存地址为[310,319]); 按以上方式,用户指令可组成32页。 (3) 计算并输出下述各种算法在不同的内存容量下的命中率。 ① 先进先出的算法(FIFO); ② 最近最少使用算法(LRR); ③ 最佳淘汰法(OPT):先淘汰最不常用的页地址; ④ 最少访问页面算法(LFR); ⑤ 最近不经常使用算法(NUR)。 其中③和④为选择内容。 命中率=1-(页面失效次数)/(页地址流长度) 在本实验中,页地址流的长度为320,页面失效次数为每次访问相应指令时,该指令所对应的页不在内存的次数。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值