页面置换算法(FIFO和LRU)

目录

题目:

FIFO:

设计思路:

代码实现:

详细思路:

LRU:

设计思路:

代码实现:

详细思路:


题目:

使用 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来存储当前遍历到的页面,如果栈空,就直接将页面调入内存,如果栈非空,但是页面不在内存中,也是将页面调入内存,如果栈非空,但是页面在内存中,就要将页面调到栈顶。如果将页面调入内存中时如果栈非空但不满,就将页面调入栈中,如果栈满,就需要进行页面置换,将栈顶调出来,将页面调入栈顶。设置一个全局变量记录页面置换次数。最后释放栈和数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值