模擬實現FIFO,LRU,OPT內存淘汰策略

模擬實現FIFO,LRU,OPT內存淘汰策略


策略簡介

  • 在頁式存儲管理中,內存以頁框爲單位分配使用。程序運行時以頁爲單位裝入內存,只把當前需要的若干頁裝入內存,且這些頁佔用的頁框不必相鄰。程序運行需要新的頁時,按需從外存上調入內存。但當物理內存中的頁不夠用的時候,要裝入新的頁就必須淘汰物理內存中某些頁框的內容.

  • FIFO:first input first output的縮寫,很容易理解這種策略的思想.

    這種算法的實質是:總是選擇在主存中停留時間最長(即最老)的一頁置換,即先進入內存的頁,先退出內存.
    理由是:最早調入內存的頁,其不再被使用的可能性比剛調入內存的可能性大.
    建立一個FIFO隊列,收容所有在內存中的頁。被置換頁面總是在隊列頭上進行。當一個頁面被放入內存時,就把它插在隊尾上.
    優點:實現簡單:頁面按進入內存的時間排序,淘汰隊頭頁面.

  • LRU:Least Recently Used,最久未使用.

    LRU算法是與每個頁面最後使用的時間有關的。當必須置換一個頁面時,LRU算法選擇過去一段時間裏最久未被使用的頁面。
    LRU算法是經常採用的頁面置換算法,,並被認爲是相當好的,但是存在如何實現它的問題.

    1.硬件方法:

    頁面設置一個移位寄存器R。每當頁面被訪問則將其重置1.
    週期性地(週期很短)將所有頁面的R左移一位(右邊補0)
    當需要淘汰頁面時選擇R值最大的頁.
    R的位數越多且移位週期越小就越精確,但硬件成本高.

    2.計數器:

    保存時間:最簡單的情況是使每個頁表項對應一個使用時間字段,並給CPU增加一個邏輯時鐘或計數器。每次存儲訪問,該時鐘都加1。每當訪問一個頁面時,時鐘寄存器的內容就被複制到相應頁表項的使用時間字段中。這樣我們就可以始終保留着每個頁面最後訪問的“時間”。
    在置換頁面時,選擇該時間值最小的頁面。
    這樣做,不僅要查頁表,而且當頁表改變時(因CPU調度)要維護這個頁表中的時間,還要考慮到時鐘值溢出的問題。

    3.棧:

    用一個棧保留頁號。
    每當訪問一個頁面時,就把它從棧中取出放在棧頂上。這樣一來,棧頂總是放有目前使用最多的頁,而棧底放着目前最少使用的頁。
    由於要從棧的中間移走一項,所以要用具有頭尾指針的雙向鏈連起來。
    在最壞的情況下,移走一頁並把它放在棧頂上需要改動6個指針。每次修改都要有開銷,但需要置換哪個頁面卻可直接得到,用不着查找,因爲尾指針指向棧底,其中有被置換頁。

  • OPT:optimal

    該策略的思想是,淘汰不再需要或最遠的將來纔會使用到的頁面。
    特點:理論上最佳,但實踐中無法實現。因爲當缺頁發生時,操作系統無法知道各個頁面下一次是在什麼時候被訪問
    雖然這個算法不可能實現,但是最佳頁面置換算法可以用於對可實現算法的性能進行衡量比較。


模擬實現(C語言)

  • 這裏我們僅僅用簡單的數組實現。假設一虛擬頁中存放10條指令,虛擬頁數爲32,物理頁數4-32
  • 定義指令集合結構體
typedef struct commandList{
    int command;//10條指令的集合,代表所在的虛擬頁頁號
    int offset;//指令偏移地址
    int lastTimeHit;//最後一次命中的時間
    int enterNum;//模擬進入的順序
};
  • 定義物理內存頁,這裏簡單的寫入加載虛擬頁的頁號
typedef struct physicalPage{
    int command;//裝入的指令集,-1代表沒有裝入
};
  • 虛擬頁中存放些什麼數據呢?這裏我們用隨機數模擬虛擬頁中的指令.
    其中COMMAND_NUM代表指令個數,這裏將虛擬頁裝滿取320
void initVirtualPage(int * vp){
    srand((unsigned)time(NULL));
    for (int i = 0; i < COMMAND_NUM; i++)
    {
        vp[i] = rand()% COMMAND_NUM;
    //  printf("%d  ", vp[i]);
    }
}
  • 定義全局變量
//物理頁數
int physicalPageNum;
//虛擬頁,int存儲的是虛擬頁中的指令,該指令隨機產生
int* mVirtualPages;
//物理頁
physicalPage* mPhysicalPage;
//指令集
commandList* mCommandList;
//缺頁次數
int disaffect = 0;
//缺頁率
float disaffectRate;
  • 初始化變量
//初始化指令集合
void initCommandList(commandList* cl){
    for (int i = 0; i < COMMAND_NUM; i++)
    {
        //方便起見,指令集和下標即爲該指令
        (cl + i)->lastTimeHit = -1;
        (cl + i)->command = i / PAGE_CONTENT;
        (cl + i)->offset = i % PAGE_CONTENT;
        (cl + i)->enterNum = 0;
    }
}
//初始化物理內存頁
void initPhysicalPage(physicalPage* pp){
    for (int i = 0; i < physicalPageNum; i++)
    {
        (pp+i)->command = -1;
    }
}
  • 實現核心判斷:是否命中(是否存在物理頁中)
    這裏用簡單的循環判斷,若存在則返回所在位置,否則返回-1
int isExistInPhysicalPage(int command, physicalPage*pp, int length){
    for (int i = 0; i < length; i++)
    {
        if ((pp + i)->command == command) return i;
    }
    return FALSE;
}
  • FIFO策略淘汰的頁號.
int whichIsToSelectFIFO(physicalPage*pp, int length){
    int returnValue = 0;
    int target = (mCommandList + pp->command*PAGE_CONTENT)->enterNum;
    for (int i = 0; i < length; i++)
    {
        //若物理內存爲空,則直接返回該位置
        if ((pp + i)->command == -1) return i;
        //若不爲空,則返回最先進入內存的頁面
        else
        {
            int tmp = (mCommandList + (pp + i)->command*PAGE_CONTENT)->enterNum;
            if (tmp < target){
                target = tmp;
                returnValue = i;
            }
        }
    }
    return returnValue;
}

這裏將commandList中的PAGE_CONTENT倍數的指令記錄最近訪問時間
(因爲mPAGE_CONTENT到(m+1)PAGE_CONTENT-1的指令要運行,都要將m虛擬頁加載到內存中),即記錄某虛擬頁請求分配內存並進入內存的最初時間.

模擬FIFO訪問內存

void FIFO(){
    for (int i = 0; i < COMMAND_NUM; i++)
    {
        int command =* (mVirtualPages+i)/PAGE_CONTENT;
#ifdef DEBUG
        printf_s("---------------------------------------------------------------------------\n");
        printf_s("指令%d請求分配內存,", *(mVirtualPages + i));
#endif
        //是否存在物理頁?若存在,返回所在位置,否則返回-1
        int index = isExistInPhysicalPage(command, mPhysicalPage, physicalPageNum);
        // 若不存在
        if (index == FALSE){
            disaffect++;
            /*模擬執行裝入內存*/
            int targetIndex = whichIsToSelectFIFO(mPhysicalPage, physicalPageNum);
            //寫入
            (mPhysicalPage + targetIndex)->command = command;
#ifdef DEBUG
            printf_s("物理內存%d上的指令被淘汰,取而代之的是%d\n", targetIndex, *(mVirtualPages + i));
#endif
            //更新使用情況
            (mCommandList + command*PAGE_CONTENT)->enterNum = i+1;
            (mCommandList + command*PAGE_CONTENT)->lastTimeHit = i;
        }
        //否則直接更新
        else
        {
#ifdef DEBUG
            printf_s("該指令已經存在內存中!\n");
#endif
            (mCommandList + command*PAGE_CONTENT)->lastTimeHit = i;
        }
#ifdef DEBUG
        printf_s("內存使用情況:\n");
        //輸出內存使用情況
        for (int k = 0; k < physicalPageNum; k++)
        {
            printf_s(" %d ", (mPhysicalPage + k)->command);
        }
        printf_s("\n");
#endif
    }
}


for循環模擬不斷從虛擬頁取指令,取出指令賦給command(代表虛擬頁號),判斷該虛擬頁是否存在物理頁中,若存在,返回位置,並更新該虛擬頁最近命中的時間,若不存在,則根據FIFO策略找出被淘汰的內存並更新

- LRU淘汰策略

int whichIsToSelectLRU(physicalPage*pp, int length){
    int returnValue = 0;
    int target = (mCommandList + pp->command*PAGE_CONTENT)->lastTimeHit;
    for (int i = 0; i < length; i++)
    {
        //若物理內存爲空,則直接返回該位置
        if ((pp + i)->command == -1) return i;
        //若不爲空,則返回最先進入內存的頁面
        else
        {
            int tmp = (mCommandList + (pp + i)->command*PAGE_CONTENT)->lastTimeHit;
            if (tmp < target){
                target = tmp;
                returnValue = i;
            }
        }
    }
    return returnValue;
}

和FIFO一樣,用PAGE_CONTENT倍數的指令記錄虛擬頁訪問內存的信息,用指標lastTimeHit記錄最近一次被命中的時間。lastTimeHit最小,則表明中間不使用的時間越長。

模擬LRU訪問內存,模擬過程同上

void LRU(){
    for (int i = 0; i < COMMAND_NUM; i++)
    {
        int command = *(mVirtualPages + i)/PAGE_CONTENT;

#ifdef DEBUG
        printf_s("---------------------------------------------------------------------------\n");
        printf_s("指令%d請求分配內存,", *(mVirtualPages+i));
#endif

        //是否存在物理頁?若存在,返回所在位置,否則返回-1
        int index = isExistInPhysicalPage(command, mPhysicalPage, physicalPageNum);
        // 若不存在
        if (index == FALSE){
            disaffect++;
            /*模擬執行裝入內存*/
            int targetIndex = whichIsToSelectLRU(mPhysicalPage, physicalPageNum);
            //寫入
            (mPhysicalPage + targetIndex)->command = command;
#ifdef DEBUG
            printf_s("物理內存%d上的指令被淘汰,取而代之的是%d\n", targetIndex, *(mVirtualPages + i));
#endif
            //更新使用情況
            //
            (mCommandList + command*PAGE_CONTENT)->enterNum = i + 1;
            (mCommandList + command*PAGE_CONTENT)->lastTimeHit = i;
        }
        //存在內存中直接更新
        else
        {
#ifdef DEBUG
            printf_s("該指令已經存在內存中!\n");
#endif
            (mCommandList + command*PAGE_CONTENT)->lastTimeHit = i;
        }
#ifdef DEBUG
        printf_s("內存使用情況:\n");
        //輸出內存使用情況
        for (int k = 0; k < physicalPageNum; k++)
        {
            printf_s(" %d ", (mPhysicalPage + k)->command);
        }
        printf_s("\n");
#endif
    }
}
  • OPT淘汰策略
int whichIsToSelectOPT(physicalPage*pp, int length,int startNum){
    int returnValue = 0;
    int target=0;
    int tmp=-1;
    for (int i = startNum; i < COMMAND_NUM; i++){
        //若找到與當前內存相同的最近的指令 
        if (pp->command == *(mVirtualPages + i) / PAGE_CONTENT) {
            target = i - startNum + 1;
            break;
        }
        else
        {
            //否則是最遠距離
            target = COMMAND_NUM - startNum + 1;
        }
    }
    //第一個已經考慮過了
    for (int i = 1; i < length; i++)
    {
        //若物理內存爲空,則直接返回該位置
        if ((pp + i)->command == -1) return i;
        //若不爲空,則返回最先進入內存的頁面
        else
        {
            for (int k= startNum; k< COMMAND_NUM; k++){
                //若找到與當前內存相同的最近的指令 
                if ((pp+i)->command == *(mVirtualPages + k) / PAGE_CONTENT) {
                    tmp = k- startNum + 1;
                    break;
                }
                else
                {
                    //否則最遠距離 
                    tmp = COMMAND_NUM - startNum + 1;
                }
            }
            if (tmp >target){
                target = tmp;
                returnValue = i;
            }
        }
    }
    return returnValue;
}

上面的代碼片段中,從startNum開始考慮最遠的,或者是不需要的頁面

模擬OPT策略

void OPT(){
    for (int i = 0; i < COMMAND_NUM; i++)
    {
        int command = *(mVirtualPages + i) / PAGE_CONTENT;

#ifdef DEBUG
        printf_s("---------------------------------------------------------------------------\n");
        printf_s("指令%d請求分配內存,", *(mVirtualPages + i));
#endif
        //是否存在物理頁?若存在,返回所在位置,否則返回-1
        int index = isExistInPhysicalPage(command, mPhysicalPage, physicalPageNum);
        // 若不存在
        if (index == FALSE){
            //缺頁
            disaffect++;
            int targetIndex;
            /*模擬執行裝入內存*/
            //若已經到達數列末尾
            if (i + 1 == COMMAND_NUM) targetIndex = 0;
            else    targetIndex= whichIsToSelectOPT(mPhysicalPage, physicalPageNum,i+1);
            //寫入
            (mPhysicalPage + targetIndex)->command = command;
#ifdef DEBUG
            printf_s("物理內存%d上的指令被淘汰,取而代之的是%d\n", targetIndex, *(mVirtualPages + i));
#endif
            //更新使用情況
            (mCommandList + command)->enterNum = i + 1;
            (mCommandList + command)->lastTimeHit = i;
        }
        //否則直接更新
        else
        {
#ifdef DEBUG
            printf_s("該指令已經存在內存中!\n");
#endif
            (mCommandList + command)->lastTimeHit = i;
        }
#ifdef DEBUG
        printf_s("內存使用情況:\n");
        //輸出內存使用情況
        for (int k = 0; k < physicalPageNum; k++)
        {
            printf_s(" %d ", (mPhysicalPage + k)->command);
        }
        printf_s("\n");
#endif
    }
}

上面的代碼中,有一個小細節,當i到達指令末尾且未命中時,直接替代0號物理內存頁

  • 有了上面的函數後,就能計算各策略的命中率了,主函數給出如下
int _tmain(int argc, _TCHAR* argv[])
{
    while (true)
    {
        while (true)
        {
            puts("請輸入物理頁數:");
            scanf_s("%d", &physicalPageNum);
            //初始化全局變量
            if (physicalPageNum < 4 || physicalPageNum>32)
            {
                puts("輸入錯誤,請輸入大於4小於32的整數");
            }
            else
            {
                break;
            }
        }
        mPhysicalPage = (physicalPage*)malloc(physicalPageNum*sizeof(physicalPage));
        //這裏模擬32個不同的指令集合
        mCommandList = (commandList*)malloc(COMMAND_NUM*sizeof(commandList));
        mVirtualPages = (int*)malloc(COMMAND_NUM*sizeof(int));
        initCommandList(mCommandList);
        initPhysicalPage(mPhysicalPage);
        initVirtualPage(mVirtualPages);
        disaffect = 0;
        printf_s("FIFO策略:\n");
        FIFO();
        disaffectRate = (float)disaffect / (float)COMMAND_NUM;
        printf_s("命中率爲%f\n", 1 - disaffectRate);
        //重置某些數據
        initCommandList(mCommandList);
        initPhysicalPage(mPhysicalPage);
        disaffect = 0;
        printf_s("\n\nLRU策略:\n");
        LRU();
        disaffectRate = (float)disaffect / (float)COMMAND_NUM;
        printf_s("命中率爲%f\n", 1 - disaffectRate);
        //重置某些數據
        initCommandList(mCommandList);
        initPhysicalPage(mPhysicalPage);
        disaffect = 0;
        printf_s("\n\nOPT策略:\n");
        OPT();
        disaffectRate = (float)disaffect / (float)COMMAND_NUM;
        printf_s("命中率爲%f\n", 1 - disaffectRate);
    }
    system("pause");
    return 0;
}
  • 運行截圖:
    不含運行過程輸出的截圖

    含運行過程輸出的截圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章