目录
题目:
使用 C 语言设计数据结构与算法,完成 FIFO(First-In First-Out)、LRU(Least Recently Used)页面置换模拟。(1)内存页框数为3,初始为空。引用页面输入序列为 。(2)内存页框数+1,初始为空。输入 以上相同引用页面序列,分析两种算法是否会出现贝拉迪异常。
FIFO:
设计思路:
FIFO页面置换算法是要最先进入内存的页面最先被置换出去。用队列来实现,在队列中,将新的页面放在队列的尾部,每次页面置换时将新页面添加到队列的尾部,从队列的头部置换出队列,不需要页面置换时队列不动。要实现这样的功能要先有一个队列结构体以及对应的创建队列函数,判断队列是否已满,是否已空函数,还有入队,入队函数中要判断页面是否已在页面中,还要有一个打印当前队列函数方便查看当前队列,可以不用出队函数,后面会说,最后是页面置换算法。
代码实现:
#include <stdio.h>
#include <stdlib.h>
// 内存页框数
#define MAX 6
// 队列结构体
typedef struct
{
int *array;
int front, rear;
int cap;
} pagequeue;
// 创建队列
pagequeue *create_queue(int cap)
{
pagequeue *queue = (pagequeue *)malloc(sizeof(pagequeue));
// 队列创建失败
if (queue == NULL)
{
perror("队列创建失败");
exit(EXIT_FAILURE);
}
queue->array = (int *)malloc(sizeof(int) * cap);
// 数组创建失败
if (queue->array == NULL)
{
perror("数组创建失败");
exit(EXIT_FAILURE);
}
queue->front = queue->rear = -1;
queue->cap = cap;
return queue;
}
// 判断队列是否已满
int isfull(pagequeue *queue)
{
return (queue->rear + 1) % queue->cap == queue->front;
}
// 判断队列是否为空
int isempty(pagequeue *queue)
{
return queue->front == -1;
}
void printqueue(pagequeue *queue);
// 入队
int enqueue(pagequeue *queue, int item)
{
int replacepage = -1;
for (int i = 0; i < queue->cap; i++)
{
if (item == queue->array[i])
{
printf("第%d页已经在队列中 ", item);
printqueue(queue);
return -2;
}
}
// 如果队列已满
if (isfull(queue))
{
// 记录被替换的页面
replacepage = queue->array[queue->front];
queue->front = (queue->front + 1) % queue->cap;
}
// 如果队列未满
if (isempty(queue))
{
queue->front = queue->rear = 0;
}
else
{
queue->rear = (queue->rear + 1) % queue->cap;
}
queue->array[queue->rear] = item;
// 返回被替换的页面
return replacepage;
}
int ispage(pagequeue *queue, int pagenumber)
{
for (int i = queue->front; i != -1 && i != (queue->rear + 1) % queue->cap; i = (i + 1) % queue->cap)
{
if (queue->array[i] == pagenumber)
{
return 1;
}
}
return 0;
}
// 打印当前队列
void printqueue(pagequeue *queue)
{
printf("当前队列: [");
if (!isempty(queue))
{
int i = queue->front;
do
{
printf("%d", queue->array[i]);
i = (i + 1) % queue->cap;
if (i != (queue->rear + 1) % queue->cap)
{
printf(" | ");
}
} while (i != (queue->rear + 1) % queue->cap);
}
printf("]\n");
}
// 页面置换算法
void fifo(int pages[], int numpages)
{
pagequeue *pagequeue = create_queue(MAX);
int pagefifos = 0;
for (int i = 0; i < numpages; ++i)
{
int currentpage = pages[i];
int replacedpage = enqueue(pagequeue, currentpage);
// 页面已经在内存中
if (replacedpage == -2)
{
}
else if (replacedpage != -1)
{
printf("第 %d 页被第 %d 页置换. ", replacedpage, currentpage);
printqueue(pagequeue);
pagefifos++;
}
if (replacedpage == -1)
{
printf("第%d页被调入内存. ", currentpage);
printqueue(pagequeue);
}
}
printf("一共发生了%d次页面置换", pagefifos);
free(pagequeue->array);
free(pagequeue);
}
int main()
{
int pages[] = {7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1};
int numpages = sizeof(pages) / sizeof(pages[0]);
fifo(pages, numpages);
return 0;
}
详细思路:
首先定义内存页框数
然后定义队列结构体

Int *array指向整数的指针用于存储整数数组的地址,这个数组就是队列的实际存储空间,front, rear整型变量表示队列的前段和后端,cap表示队列容量。
然后是创建队列函数:

令新创建队列的front和rear初始化为-1,容量为传入的参数,如果内存分配失败会反馈错误。返回队列结构体指针。
判断队列是否已满和已空

如果尾部变量rear加一模去队列容量等于front说明队列容量已满,循环队列,rear下一个元素是fron说明队列已满
Front初始化为-1,入队时会修改front,出队时,当队列为空时会将front置为-1.所以front为-1时说明队列为空。
之后是入队函数,入队时首先判断要换入的页面是否已经在内存中

如果已经在队列中就返回-2,后面进行页面置换算法时,如果检测到入队函数返回-2,说明页面已经在队列中就什么也不做。
定义replacepage来记录被置换的页面,如果成功置换,就返回被替换的页面
![]()
之后判断队列是否已满
这时我们已经确定了我们要换入的页面不在内存中,那就是说如果队列未满,就把这个页面放到队列的尾部,如果队列已满,就把这个页面放入队列尾部的同时把队列头部调出队列,同时要把原来的除了头部的所有页面的位置往前进一位。这时原来的第一位也就是头部,被调了出去,第二位成为第一位,第三位成为第二位,以此类推,最后一个则是新调入的页面。所以入队进行页面置换的同时进行了出队操作,就不需要出队函数了。

这里依旧是取模运算,因为是循环队列。
然后是判断页面是否已经在队列中,如果是就返回1否则返回0

然后是打印当前队列函数,如果队列不是空的就打印该队列,页面之间用 | 分开

然后就是页面置换算法 fifo函数接受一个数组,里面是要置换的页面,以及数组中元素的个数,首先根据MAX创建一个队列用来进行页面置换,之后需要一个循环遍历数组,用一个整形currentpage来存储当前遍历的页面,对当前页面进行入队操作,还要用一个整形replacedpage来存储入队函数的返回值,如果replacedpage为-2,说明当前页面已经在内存中了,不许要任何操作,如果replacedpage为-1,说明没有发生页面置换,但是发生了入队操作,那就是内存未满,把页面调入内存了,如果replacedpage不是-2也不是-1说明有页面被置换了,replacedpage的值就是被置换的页面。另外需要一个整形记录页面置换的次数,最后释放队列以及队列中的数组。

LRU:
设计思路:
LRU页面置换算法是最近进入内存的页面要最先被置换出来,用栈来实现,那么越靠近栈顶就越先被置换出去,而被调入的页面也要放在栈顶。思路和FIFO页面置换算法类似,两者要被置换的页面都在头部,一个是队列头部一个是栈顶,不同的是FIFO页面置换算法被调入的页面放在队列尾部,而LRU则要放在栈顶,以及如果页面已经在内存中FIFO的处理是什么也不做,但是LRU则需要把这个页面调到栈顶,所以LRU需要一个将栈中元素移动到栈顶的函数。和FIFO页面置换算法一样,首先需要一个栈结构体,这个栈结构体依旧用数组来实现,还需要创建栈函数,判断是否栈满,空以及以及页面是否在栈中函数。还有查找函数和将页面移动到栈顶的函数。和FIFO一样,在入栈时进行出栈操作,就不需要出栈函数了。
代码实现:
#include <stdio.h>
#include <stdlib.h>
#define MAX 6
int pagelru = 0;
// 栈结构体
typedef struct
{
int *sta;
int top;
int cap;
} pagestack;
// 创建栈
pagestack *create_stack(int cap)
{
pagestack *stack = (pagestack *)malloc(sizeof(pagestack));
if (stack == NULL)
{
perror("栈创建失败");
exit(EXIT_FAILURE);
}
stack->sta = (int *)malloc(sizeof(int) * cap);
if (stack->sta == NULL)
{
perror("数组创建失败");
exit(EXIT_FAILURE);
}
stack->top = -1;
stack->cap = cap;
return stack;
}
// 判断是否栈满
int isfull(pagestack *stack)
{
return stack->top == stack->cap - 1;
}
// 判断是否栈空
int isempty(pagestack *stack)
{
return stack->top == -1;
}
// 页面是否在栈中
int ispage(pagestack *stack, int page)
{
for (int i = 0; i <= stack->top; i++)
{
if (stack->sta[i] == page)
{
return 1;
}
}
return 0;
}
// 查找序列
int findindex(pagestack *stack, int page)
{
for (int i = 0; i <= stack->top; i++)
{
if (stack->sta[i] == page)
{
return i;
}
}
return -1;
}
// 将页面移动到栈顶
void movetotop(pagestack *stack, int index)
{
int temp = stack->sta[index];
for (int i = index; i < stack->top; i++)
{
stack->sta[i] = stack->sta[i + 1];
}
stack->sta[stack->top] = temp;
}
// 打印栈
void printstack(pagestack *stack)
{
printf("当前栈:[");
for (int i = 0; i <= stack->top; i++)
{
printf("%d", stack->sta[i]);
if (i < stack->top)
{
printf(" | ");
}
}
printf("]\n");
}
// 入栈
void push(pagestack *stack, int page)
{
if (isfull(stack))
{
printf("栈已满,需要进行页面置换\n");
int removedpage = stack->sta[stack->top];
stack->sta[stack->top] = page;
printf("页面%d被置换为页面%d", removedpage, page);
pagelru++;
}
else
{
stack->sta[++(stack->top)] = page;
// 这里要写成++(stack->top)不能写成(stack->top)++,
// 前者是先递增后取值,后者是先取值后递增
}
}
// 页面置换算法
void lru(int pages[], int numpages)
{
pagestack *pagestack = create_stack(MAX);
for (int i = 0; i < numpages; i++)
{
int currentpage = pages[i];
if (isempty(pagestack) || !ispage(pagestack, currentpage))
{
printf("页面%d被调入内存 ", currentpage);
push(pagestack, currentpage);
}
else
{
printf("页面%d已经在内存中 ", currentpage);
int currentindex = findindex(pagestack, currentpage);
movetotop(pagestack, currentindex);
}
printstack(pagestack);
}
printf("一共发生了%d次页面置换", pagelru);
free(pagestack->sta);
free(pagestack);
}
int main()
{
int pages[] = {7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1};
int numpages = sizeof(pages) / sizeof(pages[0]);
lru(pages, numpages);
return 0;
}
详细思路:
首先是栈结构体,也是由数组来实现,另外有一个保存栈顶序号的整形top和栈容量cap

接着是创建栈函数,接受一个设置栈容量的参数,和创建队列函数一样,先为栈分配内存,然后为数组分配内存,top初始化为-1,容量按传入的参数设置

接着是判断栈满和占空,因为数组是从0开始,所以当top顶部等于容量cap-1时就说明栈满了,如果top为0说明有一个元素,如果top为-1,说明栈空了。

接着是判断页面是否在内存中,遍历整个数组,一个一个比对,有就返回1,没有就返回0

当页面在内存中时,还需要将这个页面调到栈顶,要先知道这个页面在数组中的位置,所以还要一个查找函数,查找函数和判断是否在内存中一样,唯一不同的是,如果遍历时找到页面了,查找函数要返回该页面在内存中的位置

查找到页面之后就要将页面移到栈顶,所以需要一个移动函数,先将目标页面取出来,然后将目标页面到栈顶的所有页面下沉一位,之后将目标页面移动到栈顶。

之后是打印栈函数,每次进行页面置换,调入内存等都要将操作后的栈内容打印出来,和队列打印一样,遍历整个栈,页面之间用|分开

之后是入栈操作,这里入栈前提是页面不在内存中,可能满也可能不满。先判断是否栈满,如果栈没满,就将页面添加到栈顶,如果栈已满就要进行页面置换,把栈顶置换出来,把新的页面调入栈顶

之后就是页面置换算法,lru函数接受两个参数,一个是数组,一个是该数组中元素个数,先用函数创建一个栈,然后遍历整个数组,用currentpage来存储当前遍历到的页面,如果栈空,就直接将页面调入内存,如果栈非空,但是页面不在内存中,也是将页面调入内存,如果栈非空,但是页面在内存中,就要将页面调到栈顶。如果将页面调入内存中时如果栈非空但不满,就将页面调入栈中,如果栈满,就需要进行页面置换,将栈顶调出来,将页面调入栈顶。设置一个全局变量记录页面置换次数。最后释放栈和数组。


&spm=1001.2101.3001.5002&articleId=135949672&d=1&t=3&u=4c6e888f050f46f29caea63e55d7cfa7)
692

被折叠的 条评论
为什么被折叠?



