因为是模拟程序,可以不使用系统调用函数。
1、 画出每个页面置换算法流程图;
2、 对算法所用的数据结构进行说明;
#define INVALID -1
#define total_instruction 320 // 指令流长度
#define total_vp 32 // 虚页长
#define clear_period 50 // 清0周期
typedef struct // 页面结构
{
int pn; // 页号
int pfn; // 页面
int counter; // 一个周期内访问该页面的次数
int time; // 访问时间
}pl_type;
pl_type pl[total_vp]; // 页面结构数组
struct pfc_struct // 页面控制结构
{
int pn; // 页号
int pfn; // 页面
struct pfc_struct *next; // 指向下一个页面的指针
};
typedef struct pfc_struct pfc_type;
pfc_type pfc[total_vp]; // 定义用户进程虚页控制结构
pfc_type *freepf_head; // 空页面的头指针
pfc_type *busypf_head; // 忙页面的头指针
pfc_type *busypf_tail; // 忙页面的尾指针
int diseffect; // 页面实效次数
int a[total_instruction]; // 指令流数据组
int page[total_instruction]; // 每条指令所属页号
int offset[total_instruction]; // 每页装入10条指令后取模运算页号偏移值
float diseffect_vag[3]; // 三种算法的平均命中率
3、 测试数据随机产生。不可手工输入;
4、 编写程序并调试;
#include
#include
#include
#define INVALID -1
#define total_instruction 320 // 指令流长度
#define total_vp 32 // 虚页长
#define clear_period 50 // 清0周期
typedef struct // 页面结构
{
int pn; // 页号
int pfn; // 页面
int counter; // 一个周期内访问该页面的次数
int time; // 访问时间
}pl_type;
pl_type pl[total_vp]; // 页面结构数组
struct pfc_struct // 页面控制结构
{
int pn; // 页号
int pfn; // 页面
struct pfc_struct *next; // 指向下一个页面的指针
};
typedef struct pfc_struct pfc_type;
pfc_type pfc[total_vp]; // 定义用户进程虚页控制结构
pfc_type *freepf_head; // 空页面的头指针
pfc_type *busypf_head; // 忙页面的头指针
pfc_type *busypf_tail; // 忙页面的尾指针
int diseffect; // 页面实效次数
int a[total_instruction]; // 指令流数据组
int page[total_instruction]; // 每条指令所属页号
int offset[total_instruction]; // 每页装入10条指令后取模运算页号偏移值
float diseffect_vag[3]; // 三种算法的平均命中率
int init(int); // 初始化,为每个页面赋初值
int OPT(int); // 最佳淘汰置换算法
int FIFO(int); // 先进先出置换算法算法
int LRU(int); // 最近最久未使用置换算法
int main()
{
int s, i, j;
srand(time(0));
//在[0,319]的指令地址之间随机选取一起点m
s = (float)319 * rand() / 32767 / 32767 / 2 + 1;
for (i = 0; i < total_instruction; i += 4) //产生指令队列
{
a[i] = s; //任选一指令访问点m
a[i + 1] = a[i] + 1; //顺序执行一条指令
a[i + 2] = (float)a[i] * rand() / 32767 / 32767 / 2; //执行前地址指令m'
a[i + 3] = a[i + 2] + 1; //m'+1
s = (float)(318 - a[i + 2]) * rand() / 32767 / 32767 / 2 + a[i + 2] + 2; //在后地址[m’+2,319]中随机选取一条指令并执行
//printf("i=%d----%d\n", i, s);
}
for (i = 0; i < total_instruction; i++) //将指令序列变换成页地址流
{
page[i] = a[i] / 10;
offset[i] = a[i] % 10;
}
printf("页号\\命中率: OPT\tFIFO\tLRU\n");
for (i = 4; i <= 32; i++) //用户内存工作区从4个页面到32个页面
{
printf(" %2d \t\t", i);
OPT(i);
FIFO(i);
LRU(i);
printf("\n");
}
printf("平均命中率:\t");
for (i = 0; i < 3; i++)
{
printf("%6.4f\t", diseffect_vag[i] / 29);
}
printf("\n");
return 0;
}
int init(total_pf) //初始化相关数据结构
int total_pf; //用户进程的内存页面数
{
int i;
diseffect = 0;
for (i = 0; i < total_vp; i++)
{
pl[i].pn = i;
pl[i].pfn = INVALID; //置页面控制结构中的页号,页面为空
pl[i].counter = 0;
pl[i].time = -1; //页面控制结构中的访问次数为0,时间为-1
}
for (i = 0; i < total_pf - 1; i++)
{
pfc[i].next = &pfc[i + 1];
pfc[i].pfn = i;
} //建立pfc[i-1]和pfc[i]之间的链接
pfc[total_pf - 1].next = NULL;
pfc[total_pf - 1].pfn = total_pf - 1;
freepf_head = &pfc[0]; //空页面队列的头指针为pfc[0]
return 0;
}
int OPT(total_pf) //最佳置换算法
int total_pf;
{
int i, j, max, maxpage, d, dist[total_vp];
pfc_type* t;
init(total_pf);
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] = 32767; //最大"距离"
else
dist[j] = 0;
d = 1;
for (j = i + 1; j < total_instruction; j++)
{
if (pl[page[j]].pfn != INVALID)
dist[page[j]] = d;
d++;
}
max = -1;
for (j = 0; j < total_vp; j++)
if (max < dist[j])
{
max = dist[j];
maxpage = j;
}
freepf_head = &pfc[pl[maxpage].pfn];
freepf_head->next = NULL;
pl[maxpage].pfn = INVALID;
}
pl[page[i]].pfn = freepf_head->pfn;
freepf_head = freepf_head->next;
}
}
diseffect_vag[0] += 1 - (float)diseffect / 320;
printf("%6.4f\t", 1 - (float)diseffect / 320);
return 0;
}
int FIFO(total_pf) //先进先出算法
int total_pf; //用户进程的内存页面数
{
int i, j;
pfc_type* p;
init(total_pf); //初始化相关页面控制用数据结构
busypf_head = busypf_tail = NULL; //忙页面队列头,队列尾链接
for (i = 0; i < total_instruction; i++)
{
if (pl[page[i]].pfn == INVALID) //页面失效
{
diseffect += 1; //失效次数
if (freepf_head == NULL) //无空闲页面
{
p = busypf_head->next;
pl[busypf_head->pn].pfn = INVALID;
freepf_head = busypf_head; //释放忙页面队列的第一个页面
freepf_head->next = NULL;
busypf_head = p;
}
p = freepf_head->next; //按FIFO方式调新页面入内存页面
freepf_head->next = NULL;
freepf_head->pn = page[i];
pl[page[i]].pfn = freepf_head->pfn;
if (busypf_tail == NULL)
busypf_head = busypf_tail = freepf_head;
else
{
busypf_tail->next = freepf_head; //free页面减少一个
busypf_tail = freepf_head;
}
freepf_head = p;
}
}
diseffect_vag[1] += 1 - (float)diseffect / 320;
printf("%6.4f\t", 1 - (float)diseffect / 320);
return 0;
}
int LRU (total_pf) //最近最久未使用算法
int total_pf;
{
int min, minj, i, j, present_time;
init(total_pf);
present_time = 0;
for (i = 0; i < total_instruction; i++)
{
if (pl[page[i]].pfn == INVALID) //页面失效
{
diseffect++;
if (freepf_head == NULL) //无空闲页面
{
min = 32767;
for (j = 0; j < total_vp; j++) //找出time的最小值
if (min > pl[j].time && pl[j].pfn != INVALID)
{
min = pl[j].time;
minj = j;
}
freepf_head = &pfc[pl[minj].pfn]; //腾出一个单元
pl[minj].pfn = INVALID;
pl[minj].time = -1;
freepf_head->next = NULL;
}
pl[page[i]].pfn = freepf_head->pfn; //有空闲页面,改为有效
pl[page[i]].time = present_time;
freepf_head = freepf_head->next; //减少一个free 页面
}
else
pl[page[i]].time = present_time; //命中则增加该单元的访问次数
present_time++;
}
diseffect_vag[2] += 1 - (float)diseffect / 320;
printf("%6.4f\t", 1 - (float)diseffect / 320);
return 0;
}
从大量的实验结果中得出,最佳置换算法(OPT)拥有最高的命中率,而先进先出置换算法(FIFO)和最近最久未使用(LRU)算法的命中率相差不大,先进先出置换算法(FIFO)在某种情况下命中率比最近最久未使用(LRU)算法命中率高,但是在某种情况下最近最久未使用(LRU)算法命中率又比先进先出置换算法(FIFO)命中率高。
(1)、 最佳置换算法(OPT)
最优置换(Optimal Replacement)是在理论上提出的一种算法。其实质是:当调入新的一页而必须预先置换某个老页时,所选择的老页应是将来不再被使用,或者是在最远的将来才被访问。采用这种页面置换算法,保证有最少的缺页率。
但是最优页面置换算法的实现是困难的,因为它需要人们预先就知道一个进程整个运行过程中页面走向的全部情况。
(2)、 先进先出置换算法(FIFO)
最简单的页面置换算法是先入先出(FIFO)法。这种算法的实质是,总是选择在主存中停留时间最长(即最老)的一页置换,即先进入内存的页,先退出内存。理由是:最早调入内存的页,其不再被使用的可能性比刚调入内存的可能性大。建立一个FIFO队列,收容所有在内存中的页。被置换页面总是在队列头上进行。当一个页面被放入内存时,就把它插在队尾上。
这种算法只是在按线性顺序访问地址空间时才是理想的,否则效率不高。因为那些常被访问的页,往往在主存中也停留得最久,结果它们因变“老”而不得不被置换出去。
FIFO的另一个缺点是,它有一种异常现象,即在增加存储块的情况下,反而使缺页中断率增加了。
(3)、 最近最久未使用(LRU)算法
FIFO算法和OPT算法之间的主要差别是,FIFO算法利用页面进入内存后的时间长短作为置换依据,而OPT算法的依据是将来使用页面的时间。如果以最近的过去作为不久将来的近似,那么就可以把过去最长一段时间里不曾被使用的页面置换掉。它的实质是,当需要置换一页时,选择在最近一段时间里最久没有使用过的页面予以置换。这种算法就称为最久未使用算法(Least Recently Used,LRU)。
LRU算法是与每个页面最后使用的时间有关的。当必须置换一个页面时,LRU算法选择过去一段时间里最久未被使用的页面。LRU算法是经常采用的页面置换算法,并被认为是相当好的,但是存在如何实现它的问题。LRU算法需要实际硬件的支持。
1、从几种算法的命中率看,哪个算法最高?哪个算法最低?对每个页面的执行结果进行分析。
答:由实验结果截图,从几种算法的命中率看,OPT最高,有时FIFO最低,有时与LRU最低,每个页面执行结果会有所不同,但是FIFO与LRU相差不大。
最佳置换算法(Optimal):它所选择的被淘汰页面,将是在最长(未来)时间内不再被访问的或则是以后永不使用的页面。所以使用该置换算法,通常可以使得缺页率最低。
先进先出(FIFO)页面置换算法:该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。
最近最少使用(LRU)置换算法:算法根据数据的历史访问记录来进行淘汰数据,把历史访问记录的最久没有被访问到的数据淘汰。
因为先进先出(FIFO)页面置换算法和最近最少使用(LRU)置换算法都是根据历史数据选择页面置换,是利用“最近的过去”作为“最近的将来”的近似,所以具有一定的不确定性,因而缺页率通常也较大。而最佳置换算法(Optimal)是根据未来的数据选择页面置换,因此该算法的缺页率最低。
2、OPT算法在执行过程中可能会发生错误,为什么?
最佳置换算法(Optimal是选择以后永不使用的或许是在最长(未来)时间内不再被访问的页面淘汰。但由于计算机目前还无法预知一个进程在内存的若干个页面中,哪一个页面是未来最长时间内不再被访问的,因而该算法是无法实现的。