模拟实现请求分页虚存页面替换算法

时间:2024-03-25 15:21:11

模拟实现请求分页虚存页面替换算法

1. 需求分析

请求分页虚存管理在地址映射过程中,若页表中发现所要访问的页不在主存,则产生缺页异常,操作系统接到此信号后,就调出缺页异常处理程序,根据页表中给出的磁盘地址,将该页面调入主存,是作业继续运行下去。如果主存中有空闲块,则分配一个页框,将新调入页面装入,并修改页表中相应页表项的驻留位及相应的主存块号;若此时主存中没有空闲块,则要淘汰某页面,若该页在此期间被修改过,要将其先写回磁盘,这个过程就是页面替换。

页面替换算法是虚拟存储管理实现的关键,好的算法能够减少和避免“颠簸”现象,本程序在模拟实现FIFO,LRU和OPT几种经典页面替换算法的基础上,比较各种替换算法的效率及优缺点,从而了解虚拟存储实现的过程,理解内存页面调度的机制。

2. 数据结构设计

  • 在请求分页虚存页面替换算法中,为实现从请求页到主存块的替换,需要在模拟程序中维护两个数据结构,即请求页面队列和主存块队列。
  • 请求页面队列为进程所用,记录当前进程请求的页面块信息。
  • 主存队列由系统维护,该队列保存当前系统中各主存块的状态(包括最后访问时间、闲忙状态等)。
  • 以这两个数据结构为基础,实现各种替换算法,在系统中为用户请求寻找物理块。
  • 本程序设计含有以下功能:
    • 接收用户输入参数,包括程序长度(页面数)、页框个数及页面访问序列。
    • 程序结果采用不同的标志区分命中、替换及直接加入空闲块。
    • 实现OPT、FIFO、LRU等替换算法,并显示算法对应替换页面的过程
    • 计算各种页面替换算法的缺页中断率。

3. 程序设计

3.1 页面替换算法基本思路

本程序并没有进入系统空间对实际进程页面进行控制,而是在用户空间用线性表的连续存储方式对进程页面替换进行模拟。

  1. 最佳页面替换算法(OPT)

    淘汰以后不再需要的或者最远的将来才会用到的页面。

  2. 先进先出页面替换算法(FIFO)

    淘汰最先调入主存的页面,或者说在主存中驻留时间最长的那一页。

  3. 最近最少使用页面替换算法(LRU)

    淘汰在最近一段时间里较久未被访问的页面。它是根据程序执行时所具有的局部性来考虑的,即那些刚被使用过的页面可能马上还要被使用,而那些在较长时间里未被使用的页面一般可能不会马上使用。

3.2 请求页面队列

typedef struct _Page { // 页面
    int pageID;     //页号
} Page;
typedef struct _PageQueue { //页面队列
    Page page;
    struct _PageQueue *next; //下一页面
} PageQueue;
typedef struct process { // 进程结构
    PageQueue pages; //页面
    unsigned int pageLength; // 页面数
} process;//进程

3.3 主存块队列

typedef struct _Block { //块记录结构
    Page *page; //页面
    long time; //最后访问时间
    int state; //页块是否空闲
} Block;
typedef struct _BlockQueue { //块队列
    Block block;
    struct _BlockQueue *next;
} BlockQueue;

3.4 进程

初始化进程以及进程需要访问的页面的序列,这里仅仅展示了进程接收输入序列作为页面访问序列的代码,本程序还提供了由系统自动生成的页面访问序列,可以指定访问页面的最大序号。

PageQueue *InitializePageQueueWithInput(unsigned int pageLength) {
    //初始化页面,把首地址返回。如果分配失败,返回NULL
    PageQueue *head = NULL, *p = NULL, *q = NULL;
    for (int i = 0; i < pageLength; i++) {
        p = (PageQueue *) malloc(sizeof(PageQueue));
        p->page.pageID = pages[i];
        p->next = NULL;
        printf("%d ", p->page.pageID);
        if (head == NULL) head = p;
        else q->next = p;
        q = p;
    }
    printf("\n");
    return head;
}

void InitializeProcessWithInput(process *proc, unsigned int pageSize) {
    //初始化进程,接收手动输入的页面访问序列
    printf("进程初始化:\n");
    proc->pageLength = pageSize;
    proc->pages.next = InitializePageQueueWithInput(pageSize);
}

随机生成页面访问序列

PageQueue *InitializePageQueue(unsigned int pageLength, int maxPageID) {
    //初始化页面,把首地址返回。如果分配失败,返回NULL
    srand(100);
    PageQueue *head = NULL, *p = NULL, *q = NULL;
    for (int i = 0; i < pageLength; i++) {
        p = (PageQueue *) malloc(sizeof(PageQueue));
        p->page.pageID = rand() % (maxPageID + 1);
        p->next = NULL;
        printf("%d ", p->page.pageID);
        if (head == NULL) head = p;
        else q->next = p;
        q = p;
    }
    printf("\n");
    return head;
}

3.5 主存

接收用户输入参数,作为初始化主存的块数,也即页框数,使用一个单向链表模拟实现主存。

BlockQueue *InitializeBlockQueue(unsigned int size) {
    //初始化主存块,把首地址返回,如果分配失败返回NULL
    BlockQueue *block = NULL, *p = NULL, *q = NULL;
    for (int i = 0; i < size; i++) {
        p = (BlockQueue *) malloc(sizeof(BlockQueue));
        p->block.time = 0;
        p->block.state = 0;
        p->block.page = NULL;
        p->next = NULL;
        if (block == NULL) block = p;
        else q->next = p;
        q = p;
    }
    return block;
}

3.6 主存队列的维护

这里只展示主存队列一些重要操作的代码。

BlockQueue *SearchPage(BlockQueue *blockQueue, Page page) {
    //搜索特定页面,根据页面ID进行匹配
    BlockQueue *p = blockQueue;
    while (p != NULL) {
        if (p->block.page != NULL) {
            if (p->block.page->pageID == page.pageID)
                return p;
        }
        p = p->next;
    }
    return NULL;
}
BlockQueue *SearchIdleBlock(BlockQueue *blockQueue) {
    //搜索空闲块
    BlockQueue *p = blockQueue;
    while (p != NULL) {
        if (p->block.state == IDLE) return p;
        else p = p->next;
    }
    return NULL;
}
BlockQueue *GetOldestBlock(BlockQueue *blockQueue) {
    //取得主存中停留最久的页面,返回它的地址
    BlockQueue *p = blockQueue, *oldestAddr;
    if (p == NULL) return p;
    long oldest = p->block.time;
    oldestAddr = p;
    while (p != NULL) {
        if (p->block.time < oldest) {
            oldest = p->block.time;
            oldestAddr = p;
        }
        p = p->next;
    }
    return oldestAddr;
}
BlockQueue *GetLongestWithoutAccess(BlockQueue *blockQueue, PageQueue *currentPage){
    //获取主存中最长时间将不会被访问的页面,返回其地址
    BlockQueue *p = blockQueue, *longestAddr;
    PageQueue *q = currentPage->next;
    if (p == NULL) return p;
    longestAddr = p;
    int max_count = 0;
    while (p != NULL){
        int count = 0;
        while(q != NULL){
            count++;
            if(p->block.page->pageID == q->page.pageID) break;
            q = q->next;
        }
        if(count > max_count){
            max_count = count;
            longestAddr = p;
        }
        q = currentPage->next;
        p = p->next;
    }
    return longestAddr;
}

3.7 OPT

void OPT(BlockQueue *blockQueue,process *proc){
    //最佳页面替换算法
    PageQueue *currentPage = proc->pages.next;
    while (currentPage != NULL) {
        if (SearchPage(blockQueue, currentPage->page) != NULL) {
            PrintBlockList(blockQueue, currentPage->page.pageID, COLOR_Exist);
        } else {
            BlockQueue *idleBlock = SearchIdleBlock(blockQueue);
            if (idleBlock != NULL) {
                idleBlock->block.state = BUSY;
                idleBlock->block.time = Time++;
                idleBlock->block.page = (Page *) malloc(sizeof(Page));
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_IDLE);
            } else {
                idleBlock = GetLongestWithoutAccess(blockQueue,currentPage);
                idleBlock->block.time = Time++;
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_NoIDLE);
            }
        }
        currentPage = currentPage->next;
    }
}

3.8 FIFO

void FIFO(BlockQueue *blockQueue, process *proc) {
    //先进先出算法
    PageQueue *currentPage = proc->pages.next;
    while (currentPage != NULL) {
        if (SearchPage(blockQueue, currentPage->page) != NULL) {
            PrintBlockList(blockQueue, currentPage->page.pageID, COLOR_Exist);
        } else {
            BlockQueue *idleBlock = SearchIdleBlock(blockQueue);
            if (idleBlock != NULL) {
                idleBlock->block.state = BUSY;
                idleBlock->block.time = Time++;
                idleBlock->block.page = (Page *) malloc(sizeof(Page));
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_IDLE);
            } else {
                idleBlock = GetOldestBlock(blockQueue);
                idleBlock->block.time = Time++;
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_NoIDLE);
            }
        }
        currentPage = currentPage->next;
    }
}

3.9 LRU

void LRU(BlockQueue *blockQueue, process *proc) {
    //最近最少使用
    PageQueue *currentPage = proc->pages.next;
    while (currentPage != NULL) {
        BlockQueue *searchedBlock = SearchPage(blockQueue, currentPage->page);
        if (searchedBlock != NULL) {
            searchedBlock->block.time = Time++;
            PrintBlockList(blockQueue, currentPage->page.pageID, COLOR_Exist);
        } else {
            BlockQueue *idleBlock = SearchIdleBlock(blockQueue);
            if (idleBlock != NULL) {
                idleBlock->block.state = BUSY;
                idleBlock->block.time = Time++;
                idleBlock->block.page = (Page *) malloc(sizeof(Page));
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_IDLE);
            } else {
                idleBlock = GetOldestBlock(blockQueue);
                idleBlock->block.time = Time++;
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_NoIDLE);
            }
        }
        currentPage = currentPage->next;
    }
}

4. 页面替换算法测试

测试页面访问序列(页框数为3)

7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1

模拟实现请求分页虚存页面替换算法

4.1 OPT

运行结果

---------------------------------------------------------
页框1 |  7  |  缺页! 主存不存在该页面,载入空闲页框1
页框2 |     |
页框3 |     |
---------------------------------------------------------
页框1 |  7  |
页框2 |  0  |  缺页! 主存不存在该页面,载入空闲页框2
页框3 |     |
---------------------------------------------------------
页框1 |  7  |
页框2 |  0  |
页框3 |  1  |  缺页! 主存不存在该页面,载入空闲页框3
---------------------------------------------------------
页框1 |  2  |  缺页! 主存不存在该页面,替换页框1
页框2 |  0  |
页框3 |  1  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |  命中! 主存已存在该页面,位于页框2
页框3 |  1  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |
页框3 |  3  |  缺页! 主存不存在该页面,替换页框3
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |  命中! 主存已存在该页面,位于页框2
页框3 |  3  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  4  |  缺页! 主存不存在该页面,替换页框2
页框3 |  3  |
---------------------------------------------------------
页框1 |  2  |  命中! 主存已存在该页面,位于页框1
页框2 |  4  |
页框3 |  3  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  4  |
页框3 |  3  |  命中! 主存已存在该页面,位于页框3
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |  缺页! 主存不存在该页面,替换页框2
页框3 |  3  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |
页框3 |  3  |  命中! 主存已存在该页面,位于页框3
---------------------------------------------------------
页框1 |  2  |  命中! 主存已存在该页面,位于页框1
页框2 |  0  |
页框3 |  3  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |
页框3 |  1  |  缺页! 主存不存在该页面,替换页框3
---------------------------------------------------------
页框1 |  2  |  命中! 主存已存在该页面,位于页框1
页框2 |  0  |
页框3 |  1  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |  命中! 主存已存在该页面,位于页框2
页框3 |  1  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |
页框3 |  1  |  命中! 主存已存在该页面,位于页框3
---------------------------------------------------------
页框1 |  7  |  缺页! 主存不存在该页面,替换页框1
页框2 |  0  |
页框3 |  1  |
---------------------------------------------------------
页框1 |  7  |
页框2 |  0  |  命中! 主存已存在该页面,位于页框2
页框3 |  1  |
---------------------------------------------------------
页框1 |  7  |
页框2 |  0  |
页框3 |  1  |  命中! 主存已存在该页面,位于页框3
---------------------------------------------------------
缺页中断率为:45.00%

运行结果部分截图

模拟实现请求分页虚存页面替换算法

对比准确数据

模拟实现请求分页虚存页面替换算法

4.2 FIFO

---------------------------------------------------------
页框1 |  7  |  缺页! 主存不存在该页面,载入空闲页框1
页框2 |     |
页框3 |     |
---------------------------------------------------------
页框1 |  7  |
页框2 |  0  |  缺页! 主存不存在该页面,载入空闲页框2
页框3 |     |
---------------------------------------------------------
页框1 |  7  |
页框2 |  0  |
页框3 |  1  |  缺页! 主存不存在该页面,载入空闲页框3
---------------------------------------------------------
页框1 |  2  |  缺页! 主存不存在该页面,替换页框1
页框2 |  0  |
页框3 |  1  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |  命中! 主存已存在该页面,位于页框2
页框3 |  1  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  3  |  缺页! 主存不存在该页面,替换页框2
页框3 |  1  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  3  |
页框3 |  0  |  缺页! 主存不存在该页面,替换页框3
---------------------------------------------------------
页框1 |  4  |  缺页! 主存不存在该页面,替换页框1
页框2 |  3  |
页框3 |  0  |
---------------------------------------------------------
页框1 |  4  |
页框2 |  2  |  缺页! 主存不存在该页面,替换页框2
页框3 |  0  |
---------------------------------------------------------
页框1 |  4  |
页框2 |  2  |
页框3 |  3  |  缺页! 主存不存在该页面,替换页框3
---------------------------------------------------------
页框1 |  0  |  缺页! 主存不存在该页面,替换页框1
页框2 |  2  |
页框3 |  3  |
---------------------------------------------------------
页框1 |  0  |
页框2 |  2  |
页框3 |  3  |  命中! 主存已存在该页面,位于页框3
---------------------------------------------------------
页框1 |  0  |
页框2 |  2  |  命中! 主存已存在该页面,位于页框2
页框3 |  3  |
---------------------------------------------------------
页框1 |  0  |
页框2 |  1  |  缺页! 主存不存在该页面,替换页框2
页框3 |  3  |
---------------------------------------------------------
页框1 |  0  |
页框2 |  1  |
页框3 |  2  |  缺页! 主存不存在该页面,替换页框3
---------------------------------------------------------
页框1 |  0  |  命中! 主存已存在该页面,位于页框1
页框2 |  1  |
页框3 |  2  |
---------------------------------------------------------
页框1 |  0  |
页框2 |  1  |  命中! 主存已存在该页面,位于页框2
页框3 |  2  |
---------------------------------------------------------
页框1 |  7  |  缺页! 主存不存在该页面,替换页框1
页框2 |  1  |
页框3 |  2  |
---------------------------------------------------------
页框1 |  7  |
页框2 |  0  |  缺页! 主存不存在该页面,替换页框2
页框3 |  2  |
---------------------------------------------------------
页框1 |  7  |
页框2 |  0  |
页框3 |  1  |  缺页! 主存不存在该页面,替换页框3
---------------------------------------------------------
缺页中断率为:75.00%

运行结果部分截图

模拟实现请求分页虚存页面替换算法

对比准确数据

模拟实现请求分页虚存页面替换算法

4.3 LRU

---------------------------------------------------------
页框1 |  7  |  缺页! 主存不存在该页面,载入空闲页框1
页框2 |     |
页框3 |     |
---------------------------------------------------------
页框1 |  7  |
页框2 |  0  |  缺页! 主存不存在该页面,载入空闲页框2
页框3 |     |
---------------------------------------------------------
页框1 |  7  |
页框2 |  0  |
页框3 |  1  |  缺页! 主存不存在该页面,载入空闲页框3
---------------------------------------------------------
页框1 |  2  |  缺页! 主存不存在该页面,替换页框1
页框2 |  0  |
页框3 |  1  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |  命中! 主存已存在该页面,位于页框2
页框3 |  1  |
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |
页框3 |  3  |  缺页! 主存不存在该页面,替换页框3
---------------------------------------------------------
页框1 |  2  |
页框2 |  0  |  命中! 主存已存在该页面,位于页框2
页框3 |  3  |
---------------------------------------------------------
页框1 |  4  |  缺页! 主存不存在该页面,替换页框1
页框2 |  0  |
页框3 |  3  |
---------------------------------------------------------
页框1 |  4  |
页框2 |  0  |
页框3 |  2  |  缺页! 主存不存在该页面,替换页框3
---------------------------------------------------------
页框1 |  4  |
页框2 |  3  |  缺页! 主存不存在该页面,替换页框2
页框3 |  2  |
---------------------------------------------------------
页框1 |  0  |  缺页! 主存不存在该页面,替换页框1
页框2 |  3  |
页框3 |  2  |
---------------------------------------------------------
页框1 |  0  |
页框2 |  3  |  命中! 主存已存在该页面,位于页框2
页框3 |  2  |
---------------------------------------------------------
页框1 |  0  |
页框2 |  3  |
页框3 |  2  |  命中! 主存已存在该页面,位于页框3
---------------------------------------------------------
页框1 |  1  |  缺页! 主存不存在该页面,替换页框1
页框2 |  3  |
页框3 |  2  |
---------------------------------------------------------
页框1 |  1  |
页框2 |  3  |
页框3 |  2  |  命中! 主存已存在该页面,位于页框3
---------------------------------------------------------
页框1 |  1  |
页框2 |  0  |  缺页! 主存不存在该页面,替换页框2
页框3 |  2  |
---------------------------------------------------------
页框1 |  1  |  命中! 主存已存在该页面,位于页框1
页框2 |  0  |
页框3 |  2  |
---------------------------------------------------------
页框1 |  1  |
页框2 |  0  |
页框3 |  7  |  缺页! 主存不存在该页面,替换页框3
---------------------------------------------------------
页框1 |  1  |
页框2 |  0  |  命中! 主存已存在该页面,位于页框2
页框3 |  7  |
---------------------------------------------------------
页框1 |  1  |  命中! 主存已存在该页面,位于页框1
页框2 |  0  |
页框3 |  7  |
---------------------------------------------------------
缺页中断率为:60.00%

运行结果部分截图

模拟实现请求分页虚存页面替换算法

对比准确数据

模拟实现请求分页虚存页面替换算法

5. 总结

分页虚拟存储管理既有优点又有缺点,优点是作业的程序和数据可按页分散存放在主存中,有效解决了碎片问题; 既有利于改进主存利用率,又有利于多道程序运行。缺点是要有硬件支持,要进行缺页中断处理,机器成本增加,系统开销加大。缺页中断率小是虚拟存储管理目标之一,而影响缺页中断率的因素主要有:主存页框数、页面大小、页面分配机制,替换算法、程序的局部性。好的页面替换算法能够降低缺页中断率。

6. 完整源码

//
// Created by wylu on 2018/1/6.
//

#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<sys/time.h>
#include<unistd.h>
#include<stdlib.h>

#define BUSY 1
#define IDLE 0

#define COLOR_Exist 0
#define COLOR_NotExist_IDLE 1
#define COLOR_NotExist_NoIDLE 2

double hitCount = 0;
int pages[100];

typedef struct _Page { // 页面
    int pageID;     //页号
} Page;
typedef struct _PageQueue { //页面队列
    Page page;
    struct _PageQueue *next; //下一页面
} PageQueue;
typedef struct _Block { //块记录结构
    Page *page; //页面
    long time; //最后访问时间
    int state; //页块是否空闲
} Block;
typedef struct _BlockQueue { //块队列
    Block block;
    struct _BlockQueue *next;
} BlockQueue;
typedef struct process { // 进程结构
    PageQueue pages; //页面
    unsigned int pageLength; // 页面数
} process;//进程

int Time = 0;

long GetSystemUtime() {
    //获取系统当前时间的微秒数
    struct timeval nowit;
    gettimeofday(&nowit, NULL);
    return 1000000 * nowit.tv_sec + nowit.tv_usec;;
}

BlockQueue *InitializeBlockQueue(unsigned int size) {
    //初始化主存块,把首地址返回,如果分配失败返回NULL
    BlockQueue *block = NULL, *p = NULL, *q = NULL;
    for (int i = 0; i < size; i++) {
        p = (BlockQueue *) malloc(sizeof(BlockQueue));
        p->block.time = 0;
        p->block.state = 0;
        p->block.page = NULL;
        p->next = NULL;
        if (block == NULL) block = p;
        else q->next = p;
        q = p;
    }
    return block;
}

int GetBlockQueueSize(BlockQueue *blockQueue) {
    //获取块长度
    BlockQueue *presentBlock = blockQueue;
    int blockQueueSize = 0;
    while (presentBlock != NULL) {
        blockQueueSize++;
        presentBlock = presentBlock->next;
    }
    return blockQueueSize;
}

void ResetBlockQueue(BlockQueue *blockQueue) {
    //清空块内容
    BlockQueue *presentBlock = blockQueue;
    while (presentBlock != NULL) {
        presentBlock->block.page = NULL;
        presentBlock->block.state = IDLE;
        presentBlock->block.time = 0;
        presentBlock = presentBlock->next;
    }
}

PageQueue *InitializePageQueue(unsigned int pageLength, int maxPageID) {
    //初始化页面,把首地址返回。如果分配失败,返回NULL
    srand(100);
    PageQueue *head = NULL, *p = NULL, *q = NULL;
    for (int i = 0; i < pageLength; i++) {
        p = (PageQueue *) malloc(sizeof(PageQueue));
        p->page.pageID = rand() % (maxPageID + 1);
        p->next = NULL;
        printf("%d ", p->page.pageID);
        if (head == NULL) head = p;
        else q->next = p;
        q = p;
    }
    printf("\n");
    return head;
}

PageQueue *InitializePageQueueWithInput(unsigned int pageLength) {
    //初始化页面,把首地址返回。如果分配失败,返回NULL
    PageQueue *head = NULL, *p = NULL, *q = NULL;
    for (int i = 0; i < pageLength; i++) {
        p = (PageQueue *) malloc(sizeof(PageQueue));
        p->page.pageID = pages[i];
        p->next = NULL;
        printf("%d ", p->page.pageID);
        if (head == NULL) head = p;
        else q->next = p;
        q = p;
    }
    printf("\n");
    return head;
}

void InitializeProcess(process *proc, unsigned int pageSize, unsigned int maxPageID) {
    //初始化进程,随机生成进程页面访问序列
    printf("进程初始化:\n");
    proc->pageLength = pageSize;
    proc->pages.next = InitializePageQueue(pageSize, maxPageID);
}

void InitializeProcessWithInput(process *proc, unsigned int pageSize) {
    //初始化进程,接收手动输入的页面访问序列
    printf("进程初始化:\n");
    proc->pageLength = pageSize;
    proc->pages.next = InitializePageQueueWithInput(pageSize);
}

BlockQueue *SearchPage(BlockQueue *blockQueue, Page page) {
    //搜索特定页面
    BlockQueue *p = blockQueue;
    while (p != NULL) {
        if (p->block.page != NULL) {
            if (p->block.page->pageID == page.pageID)
                return p;
        }
        p = p->next;
    }
    return NULL;
}

BlockQueue *SearchIdleBlock(BlockQueue *blockQueue) {
    //搜索空闲块
    BlockQueue *p = blockQueue;
    while (p != NULL) {
        if (p->block.state == IDLE) return p;
        else p = p->next;
    }
    return NULL;
}

int GetBlockLable(BlockQueue *blockQueue, BlockQueue *goalBlock) {
    //返回块号,编号从1开始
    BlockQueue *p = blockQueue;
    int count = 1;
    while (p != goalBlock) {
        p = p->next;
        count++;
    }
    return count;
}

BlockQueue *GetOldestBlock(BlockQueue *blockQueue) {
    //取得主存中停留最久的页面,返回它的地址
    BlockQueue *p = blockQueue, *oldestAddr;
    if (p == NULL) return p;
    long oldest = p->block.time;
    oldestAddr = p;
    while (p != NULL) {
        if (p->block.time < oldest) {
            oldest = p->block.time;
            oldestAddr = p;
        }
        p = p->next;
    }
    return oldestAddr;
}

BlockQueue *GetLongestWithoutAccess(BlockQueue *blockQueue, PageQueue *currentPage){
    //获取主存中最长时间将不会被访问的页面,返回其地址
    BlockQueue *p = blockQueue, *longestAddr;
    PageQueue *q = currentPage->next;
    if (p == NULL) return p;
    longestAddr = p;
    int max_count = 0;
    while (p != NULL){
        int count = 0;
        while(q != NULL){
            count++;
            if(p->block.page->pageID == q->page.pageID) break;
            q = q->next;
        }
        if(count > max_count){
            max_count = count;
            longestAddr = p;
        }
        q = currentPage->next;
        p = p->next;
    }
    return longestAddr;
}

void PrintBlockList(BlockQueue *blockQueue, int pageID, int color) {
    //打印块信息
    BlockQueue *presentBlock = blockQueue;
    for (int i = 0; i < GetBlockQueueSize(blockQueue); i++) {
        if (presentBlock == NULL) break;
        printf("页框%d ", i + 1);
        if (presentBlock->block.state == IDLE) {
            printf("|     |\n");
        } else {
            if (presentBlock->block.page->pageID != pageID) {
                printf("|  %d  |\n", presentBlock->block.page->pageID);
            } else {
                switch (color) {
                    case COLOR_Exist:
                        printf("|  %d  |  命中! 主存已存在该页面,位于页框%d\n", 
                               pageID, GetBlockLable(blockQueue, presentBlock));
                        hitCount++;
                        break;
                    case COLOR_NotExist_IDLE:
                        printf("|  %d  |  缺页! 主存不存在该页面,载入空闲页框%d\n", 
                               pageID, GetBlockLable(blockQueue, presentBlock));
                        break;
                    case COLOR_NotExist_NoIDLE:
                        printf("|  %d  |  缺页! 主存不存在该页面,替换页框%d\n", 
                               pageID, GetBlockLable(blockQueue, presentBlock));
                        break;
                    default:
                        break;
                }
            }
        }
        presentBlock = presentBlock->next;
    }
    printf("---------------------------------------------------------\n");
}

void FIFO(BlockQueue *blockQueue, process *proc) {
    //先进先出算法
    PageQueue *currentPage = proc->pages.next;
    while (currentPage != NULL) {
        if (SearchPage(blockQueue, currentPage->page) != NULL) {
            PrintBlockList(blockQueue, currentPage->page.pageID, COLOR_Exist);
        } else {
            BlockQueue *idleBlock = SearchIdleBlock(blockQueue);
            if (idleBlock != NULL) {
                idleBlock->block.state = BUSY;
                idleBlock->block.time = Time++;
                idleBlock->block.page = (Page *) malloc(sizeof(Page));
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_IDLE);
            } else {
                idleBlock = GetOldestBlock(blockQueue);
                idleBlock->block.time = Time++;
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_NoIDLE);
            }
        }
        currentPage = currentPage->next;
    }
}

void LRU(BlockQueue *blockQueue, process *proc) {
    //最近最少使用
    PageQueue *currentPage = proc->pages.next;
    while (currentPage != NULL) {
        BlockQueue *searchedBlock = SearchPage(blockQueue, currentPage->page);
        if (searchedBlock != NULL) {
            searchedBlock->block.time = Time++;
            PrintBlockList(blockQueue, currentPage->page.pageID, COLOR_Exist);
        } else {
            BlockQueue *idleBlock = SearchIdleBlock(blockQueue);
            if (idleBlock != NULL) {
                idleBlock->block.state = BUSY;
                idleBlock->block.time = Time++;
                idleBlock->block.page = (Page *) malloc(sizeof(Page));
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_IDLE);
            } else {
                idleBlock = GetOldestBlock(blockQueue);
                idleBlock->block.time = Time++;
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_NoIDLE);
            }
        }
        currentPage = currentPage->next;
    }
}

void OPT(BlockQueue *blockQueue, process *proc){
    //最佳页面替换算法
    PageQueue *currentPage = proc->pages.next;
    while (currentPage != NULL) {
        if (SearchPage(blockQueue, currentPage->page) != NULL) {
            PrintBlockList(blockQueue, currentPage->page.pageID, COLOR_Exist);
        } else {
            BlockQueue *idleBlock = SearchIdleBlock(blockQueue);
            if (idleBlock != NULL) {
                idleBlock->block.state = BUSY;
                idleBlock->block.time = Time++;
                idleBlock->block.page = (Page *) malloc(sizeof(Page));
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_IDLE);
            } else {
                idleBlock = GetLongestWithoutAccess(blockQueue,currentPage);
                idleBlock->block.time = Time++;
                idleBlock->block.page->pageID = currentPage->page.pageID;
                PrintBlockList(blockQueue, 
                               currentPage->page.pageID, COLOR_NotExist_NoIDLE);
            }
        }
        currentPage = currentPage->next;
    }
}

void SelectAlgorithm(){
    printf("\n========================================================\n");
    printf("\t1.OPT\t2.FIFO\t3.LRU\t0.exit\n");
    printf("========================================================\n");
}

int main() {
    bool isGoOn = true;
    while (isGoOn){
        //主存块数,即页框数
        unsigned int blockNumber;
        //进程页面数
        unsigned int pageNumber;
        printf("请输入主存块数(即页框数):");
        scanf("%u", &blockNumber);
        printf("请输入进程页面数:");
        scanf("%u", &pageNumber);
        printf("========================================================\n");
        printf("\t主存页框数: %u, 进程页面数: %u\n", blockNumber, pageNumber);
        printf("========================================================\n");

        BlockQueue *blocks;
        process proc;
        printf("是否手动输入访问序列? y/n\n");
        getchar();
        if (getchar() == 'y'){
            printf("请输入访问序列:");
            for (int i = 0; i < pageNumber; ++i) {
                scanf("%d",&pages[i]);
            }
            InitializeProcessWithInput(&proc,pageNumber);
        } else{
            InitializeProcess(&proc, pageNumber, 10);
        }
        blocks = InitializeBlockQueue(blockNumber);

        SelectAlgorithm();
        int oper;
        while (scanf("%d",&oper)){
            int flag = 0;
            printf("\n---------------------------------------------------------\n");
            hitCount = 0;
            switch (oper){
                case 1:
                    OPT(blocks, &proc);
                    break;
                case 2:
                    FIFO(blocks, &proc);
                    break;
                case 3:
                    LRU(blocks, &proc);
                    break;
                case 0:
                    flag = 1;
                    break;
                default:
                    SelectAlgorithm();
                    printf("非法输入!请重新输入:");
                    break;
            }
            if (flag == 1) break;
            printf("缺页中断率为:%.2lf%%\n",(1-(hitCount/pageNumber))*100);
            ResetBlockQueue(blocks);
            SelectAlgorithm();
        }
        printf("是否重新输入访问序列? y/n\n");
        getchar();
        if(getchar() != 'y') isGoOn = false;
        system("cls");
    }
}