操原上机(四)

(1)报告连同源码打包发到 OSCourse@163.com,文件名:姓名学号班级OS实验报告.rar
(2)邮件主题和附件文件名:姓名学号班级OS实验报告
(3)源代码必须删除debug和release目录!
一、实验目的
此处主要粘贴四次实验指南中实验目的条目的内容。按1),2)…方式顺序编号
二、实验内容
此处主要粘贴四次实验指南中实验内容条目的内容。按1),2)…方式顺序编号
三、实验过程
此处主要粘贴核心的关键源代码和程序流程,并分析。把你做完了的全部编程题按1),2)…方式顺序编号,每个编程题目自己给取一个5-10字的名字作为标题。
四、实验结果
此处主要粘贴运行截图和分析。把你做完了的全部编程题按1),2)…方式顺序编号,每个编程题目自己给取一个5-10字的名字作为标题。
五、体会
写体会

一. 实验目的:

  1. 理解页面淘汰算法原理,编写程序演示页面淘汰算法。
  2. 验证Linux虚拟地址转化为物理地址的机制
  3. 理解和验证程序运行局部性的原理。
二. 实验内容:

1.在windows环境下编写一个程序,模拟实现OPT,FIFO,LRU等页面淘汰算法。具体实验思想和过程请参考《内存管理实验指导书》

参考:

  1. 设计一个虚拟存储区和内存工作区,编程序演示下述算法的具体实现过程,并计算访问命中率
  2. 设计一个虚拟存储区和内存工作区

法一:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define TRUE 1
#define FALSE 0
#define INVALID -1
#define NULL 0

#define total_instruction 320 //共 320 条指令
#define total_vp 32 //虚拟页共 32
#define clear_period 50

//页面类型
typedef struct {
    int pn;//页号
    int pfn;//页面号
    int counter;//一个周期内访问该页的次数
    int time;//访问时间
}pl_type;

pl_type pl[32];

//页面控制结构
typedef struct pfc_struct {
    int pn;
    int pfn;
    struct pfc_struct *next;
}pfc_type;

pfc_type pfc[32];
//空闲页头指针,忙页头指针,忙页尾指针
pfc_type *freepf_head, *busypf_head, *busypf_tail;
//缺页次数
int disaffect;
//指令流数组
int a[total_instruction];
//每条指令的页和页内偏移
int page[total_instruction], offset[total_instruction];

void initialize(int total_pf);
void FIFO(int total_pf);
void LRU(int total_pf);

int main()
{
    //用进程号作为初始化随机数队列的种子
    srand(10 * GetCurrentProcessId());
    int s = (float)319 * rand() / 32767 / 32767 / 2 + 1;
    //产生指令队列
    for (int i = 0; i < total_instruction; i++)
    {
        if (s < 0 || s > 319)
        {
            printf("when i == %d,error: s == %d\n", i, s);
            exit(0);
        }
        //任意选一指令访问点 m
        a[i] = s;
        //顺序执行下一条指令
        a[i + 1] = a[i] + 1;
        //执行前地址指令 m
        a[i + 2] = (float)a[i] * rand() / 32868 / 32767 / 2;
        //顺序执行一条指令
        a[i + 3] = a[i + 2] + 1;
        s = (float)(318 - a[i + 2]) * rand() / 32767 / 32767 / 2 + a[i + 2] + 2;
        if ((a[i + 2] > 318) || (s > 319)) {
            printf("a[%d + 2],a number which is: %d and s = %d\n", i, a[i + 2], s);
        }
        //将指令序列变成页地址流
        for (int i = 0; i < total_instruction; i++)
        {
            page[i] = a[i] / 10;
            offset[i] = a[i] % 10;
        }
        //用户内存工作区从 4 个页面到 32 个页面
        for (int i = 4; i < 32; i++)
        {
            printf("%2d page frames", i);
            //计算用 FIFO 置换时,第 i 个页面时的命中率
            FIFO(i);
            //计算用 LRU 置换时,第 i 个页面时的命中率
            LRU(i);
            printf("\n");
        }
    }
    getchar();
}

/**
 * 初始化相关数据结构
 * total_pf: 用户进程的内存页数
 */
void initialize(int total_pf) 
{
    for (int i = 0; i < total_vp; i++)
    {
        pl[i].pn = i;
        //置页面控制结构中的页号,页面号为空
        pl[i].pfn = INVALID;
        //置页面控制结构中的访问次数为 0
        pl[i].counter = 0;
        //置页面控制结构中的时间为 -1
        pl[i].time = -1;
    }
    for (int i = 0; i < total_pf - 1; i++)
    {
        pfc[i].next = &pfc[i + 1];
        pfc[i].pfn = i;
    }
    pfc[total_pf - 1].next = NULL;
    pfc[total_pf - 1].pfn = total_pf - 1;
    freepf_head = &pfc[0];
}

/**
* 先进先出
* total_pf: 用户进程的内存页数
*/
void FIFO(int total_pf)
{
    //初始化相关数据结构
    initialize(total_pf);
    busypf_head = busypf_tail = NULL;
    int disfeffect = 0;
    pfc_type *p;
    for (int i = 0; i < total_instruction; i++)
    {
        //页面失效
        if (pl[page[i]].pfn == INVALID) {
            //失效次数
            disfeffect += 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;
            }
            //按 FIFO 方式调用新页面入内存
            p = freepf_head->next;
            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
            {
                //free 页面减少一个
                busypf_tail->next = freepf_head;
                busypf_tail = freepf_head;
            }
            freepf_head = p;
        }
    }
    printf("FIFO: %6.4f", 1 - (float)disfeffect / 320);
}

/**
 * 最近最少使用
 * total_pf: 用户进程的内存页数
 */
void LRU(int total_pf) 
{
    initialize(total_pf);
    int present_time = 0;
    int diseffect = 0;
    for (int i = 0; i < total_instruction; i++)
    {
        //页面失效
        if (pl[page[i]].pfn == INVALID) {
            diseffect++;
            //无空闲页面
            if (freepf_head == NULL) {
                int min = 32767;
                int minj;
                //找出 time 的最小值
                for (int j = 0; j < total_vp; j++)
                {
                    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;
                //减少一个 free 页面
                freepf_head = freepf_head->next;
            }
            else
            {
                //命中,则增加该单元的访问次数
                pl[page[i]].time = present_time;
            }
        }
        present_time++;
    }
    printf("LRU: %6.4f", 1 - (float)diseffect / 320);
}

这里写图片描述

法二:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX_VIR_SIZE 64
#define MAX_MEM_SIZE 32
#define VIR_NO 2
#define MEM_NO 3
#define FALSE -1

int mem_frame[MAX_MEM_SIZE];        //内存帧 

int instruction[MAX_VIR_SIZE * 10];   //指令序列 
int reference[MAX_VIR_SIZE * 10];     //引用串 

int mem_size[MEM_NO] = { 4, 18, 32 }; //内存容量 
int vir_size[VIR_NO] = { 32, 64 };    //虚存容量 

/**
 * 初始化页地址流 
 */
void initialize()
{
    for (int i = 0; i < MAX_VIR_SIZE * 10; i++) {
        instruction[i] = -1;
        reference[i] = -1;
    }
}

/** 
 * 初始化内存工作区 
 */
void ini_mem()
{
    for (int i = 0; i < MAX_MEM_SIZE; i++)
        mem_frame[i] = -1;
}

/** 
 * 产生页地址流 
 */
int generate_page(int vsize)
{
    srand((unsigned)time(NULL)); /*播种子*/
    //产生指令序列 
    for (int i = 0; i < vsize * 10; i += 5) {
        instruction[i] = rand() % (vsize * 10 - 1);
        instruction[i + 1] = instruction[i] + 1;
        instruction[i + 2] = rand() % instruction[i + 1];
        instruction[i + 3] = instruction[i + 2] + 1;
        instruction[i + 4] = rand() % (vsize * 10 - instruction[i + 3] - 2) + instruction[i + 3] + 1;
    }

    //将指令序列变换为对应的页地址流
    for (int i = 0; i < vsize * 10; i++)
        instruction[i] /= 10;

    reference[0] = instruction[0];

    int base = 0, j = 1;
    for (int i = 1; i < vsize * 10; i++) {
        if (instruction[i] != instruction[base]) {
            reference[j] = instruction[i];
            j++;

            base = i;
        }
    }
    return j;    //返回引用串,即页地址流的长度 
}

/** 
 * 在内存帧中寻找页 
 */
int search(int msize, int key)
{
    for (int i = 0; i < msize; i++) {
        if (mem_frame[i] == -1)       //内存工作区未满 
            return i;
        else if (mem_frame[i] == key) //找到页对应的帧
            return i;
    }
    return FALSE;                     //内存工作区已满且没找到       
}

/** 
 * 最优页置换
 */
void OPT(int ref_len, int vsize)
{
    int mem_no, msize, i, find, miss, j, k;
    int first;
    int longest, rep;
    float rate = 0;

    printf("OPT");

    for (mem_no = 0; mem_no < MEM_NO; mem_no++) {
        miss = 0;
        ini_mem();                          //初始化内存工作区 
        msize = mem_size[mem_no];
        for (i = 0; i < ref_len; i++) {
            find = search(msize, reference[i]);
            if (find != FALSE && mem_frame[find] == -1) {     //内存工作区未满 
                miss++;
                mem_frame[find] = reference[i];//页置换 
            }
            else if (find == FALSE) {        //内存工作区已满且没找到
                miss++;
                longest = 0;
                first = 0;
                for (j = 0; j < msize; j++) {
                    for (k = i + 1; k < ref_len; k++) {
                        if (mem_frame[j] == reference[k]) {
                            if (k > longest) {
                                longest = k;
                                rep = j;     //找到向前看距离最远的一帧 
                            }
                            break;
                        }
                        if (k == ref_len && first == 0) {
                            longest = k;
                            first = 1;
                            rep = j;
                        }
                    }
                }
                mem_frame[rep] = reference[i];//页置换 
            }
            else//找到页对应的帧
                ;
        }
        rate = 1 - ((float)miss) / ((float)ref_len);          //计算命中率 
        printf("\t\t%4d/%2d\t\t%.2f\n", msize, vsize, rate);
    }
}

/** 
 * 先进先出页置换
 */
void FIFO(int ref_len, int vsize)
{
    printf("FIFO");
    for (int mem_no = 0; mem_no < MEM_NO; mem_no++) {
        int miss = 0;
        ini_mem(); //初始化内存工作区                          
        int msize = mem_size[mem_no];
        int rep = 0;
        for (int i = 0; i < ref_len; i++) {
            int find = search(msize, reference[i]);
            //内存工作区未满 
            if (find != FALSE && mem_frame[find] == -1) {     
                miss++;
                mem_frame[find] = reference[i];
            }
            //内存工作区已满且没找到
            else if (find == FALSE) {        
                miss++;
                mem_frame[rep] = reference[i];//页置换
                //下一个将要被置换的帧的位置 
                rep = (rep + 1) % msize;       
            }
            else//找到页对应的帧
                ;
        }
        float rate = 1 - ((float)miss) / ((float)ref_len);           //计算命中率 
        printf("\t\t%4d/%2d\t\t%.2f\n", msize, vsize, rate);
    }
}

//函数:最近最少使用页置换
void LRU(int ref_len, int vsize)
{
    int mem_no, msize, i, find, miss, j, k, longest, rep, dis;
    float rate = 0;

    printf("LRU");

    for (mem_no = 0; mem_no < MEM_NO; mem_no++) {
        miss = 0;
        ini_mem();  //初始化内存工作区                         
        msize = mem_size[mem_no];
        for (i = 0; i < ref_len; i++) {
            find = search(msize, reference[i]);
            //内存工作区未满 
            if (find != FALSE && mem_frame[find] == -1) {     
                miss++;
                mem_frame[find] = reference[i];//页置换 
            }
            //内存工作区已满且没找到
            else if (find == FALSE) {        
                miss++;
                longest = 0;
                for (j = 0; j < msize; j++) {
                    for (k = i - 1; k >= 0; k--) {
                        if (mem_frame[j] == reference[k]) {
                            dis = i - k;
                            if (dis > longest) {
                                longest = dis;
                                rep = j;     //找到向后看距离最远的一帧 
                            }
                            break;
                        }
                    }
                }
                mem_frame[rep] = reference[i];//页置换 
            }
            else//找到页对应的帧
                ;
        }
        rate = 1 - ((float)miss) / ((float)ref_len);          //计算命中率 
        printf("\t\t%4d/%2d\t\t%.2f\n", msize, vsize, rate);
    }
}

/** 
 * 最不经常使用页置换
 */
void LFU(int ref_len, int vsize)
{
    int mem_no, msize, i, j, find, miss, rep, least;
    //计数器:记录内存帧被使用的次数
    int count[MAX_MEM_SIZE];                   
    float rate = 0;

    printf("LFU");

    for (mem_no = 0; mem_no < MEM_NO; mem_no++) {
        miss = 0;
        ini_mem(); //初始化内存工作区                          
        msize = mem_size[mem_no];
        //初始化内存工作区每帧的计数器 
        for (i = 0; i < msize; i++)         
            count[i] = 0;
        for (i = 0; i < ref_len; i++) {
            find = search(msize, reference[i]);
            //内存工作区未满 
            if (find != FALSE && mem_frame[find] == -1) {     
                miss++;
                mem_frame[find] = reference[i];
            }
            else if (find == FALSE) {//内存工作区已满且没找到        
                miss++;
                least = count[0];
                rep = 0;
                for (j = 1; j < msize; j++) {//选择计数器值最小的帧 
                    if (count[j] < least) {
                        least = count[j];
                        rep = j;
                    }
                }
                mem_frame[rep] = reference[i];//页置换
                count[rep] = 0;
            }
            else//找到页对应的帧
                count[find] ++;

            if ((i + 1) % 32 == 0) { //计数器定期衰减               
                for (j = 0; j < msize; j++)
                    count[j] /= 2;
            }
        }
        //计算命中率 
        rate = 1 - ((float)miss) / ((float)ref_len);          
        printf("\t\t%4d/%2d\t\t%.2f\n", msize, vsize, rate);
    }
}

/** 
 * 最近未使用页置换
 */
void NUR(int ref_len, int vsize)
{
    int mem_no, msize, i, find, miss, rep, least;
    int flag[MAX_MEM_SIZE]; //访问位                  
    float rate = 0;
    int m;

    printf("NUR");

    for (mem_no = 0; mem_no < MEM_NO; mem_no++) {
        miss = 0;
        ini_mem(); //初始化内存工作区                        
        msize = mem_size[mem_no];
        //初始化内存工作区每帧的计数器 
        for (i = 0; i < msize; i++)         
            flag[i] = 0;
        rep = 0;
        for (i = 0; i < ref_len; i++) {
            find = search(msize, reference[i]);
            if (find != FALSE && mem_frame[find] == -1) { //内存工作区未满     
                miss++;
                mem_frame[find] = reference[i];
            }
            //内存工作区已满且没找到
            else if (find == FALSE) {       
                miss++;
                //最近被使用过,给第二次机会   
                while (flag[rep] != 0) {     
                    flag[rep] = 0;
                    rep = (rep + 1) % msize;
                }
                mem_frame[rep] = reference[i];//页置换
                rep = (rep + 1) % msize;
            }
            else//找到页对应的帧
                flag[find] = 1;
        }
        //计算命中率 
        rate = 1 - ((float)miss) / ((float)ref_len);          
        printf("\t\t%4d/%2d\t\t%.2f\n", msize, vsize, rate);
    }
}

#if 1
int main(int argc, char *argv[])
{
    int vir_no, vsize, ref_len;

    //设置随机数种子 
    srand(time(NULL));

    for (vir_no = 0; vir_no < VIR_NO; vir_no++) {
        initialize();  //初始化虚拟存储区 
        vsize = vir_size[vir_no]; //获取虚拟存储区大小  

        ref_len = generate_page(vsize);

        printf("Algorithm\tMemory/Virtual\tRate\n");
        //最优页置换(OPT) 
        OPT(ref_len, vsize);
        //先进先出页置换(FIFO)
        FIFO(ref_len, vsize);
        //最近最少使用页置换(LRU)
        LRU(ref_len, vsize);
        //最不经常使用页置换(LFU)
        LFU(ref_len, vsize);
        //最近未使用页置换(NUR)
        NUR(ref_len, vsize);

        printf("\n");
    }
    scanf("%d");
    return 0;
}
#endif // 0

这里写图片描述

2.在Linux环境下,编写一个小程序,获取该程序中的某个变量的虚拟地址,虚拟页号,页内偏移地址,物理页框号,页内偏移地址,物理地址,并将它们打印出来。建议使用/proc/pid/pagemap技术。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/wait.h>

void mem_addr(unsigned long vaddr, unsigned long *paddr)
{
    int pageSize = getpagesize(); //调用此函数获取系统设定的页面大小
    printf("页面大小为%d\n", pageSize);

    unsigned long v_pageIndex = vaddr / pageSize; //计算此虚拟地址相对于0x0的经过的页面数
    printf("页面数为:%u\n", v_pageIndex);
    unsigned long v_offset = v_pageIndex * sizeof(uint64_t); //计算在/proc/pid/page_map文件中的偏移量
    unsigned long page_offset = vaddr % pageSize;            //计算虚拟地址在页面中的偏移量
    printf("偏移量为:%x\n", page_offset);
    uint64_t item = 0;                             //存储对应项的值
    int fd = open("/proc/self/pagemap", O_RDONLY); //以只读方式打开/proc/pid/page_map

    lseek(fd, v_offset, SEEK_SET);                         //将游标移动到相应位置,即对应项的起始地址且判断是否移动失败
    read(fd, &item, sizeof(uint64_t)) != sizeof(uint64_t); //读取对应项的值,并存入item中,且判断读取数据位数是否正确

    if ((((uint64_t)1 << 63) & item) == 0) //判断present是否为0
    {
        printf("page present is 0\n");
        return;
    }
    printf("物理页号为%u\n", ((uint64_t)1 << 63) & item);
    uint64_t phy_pageIndex = (((uint64_t)1 << 55) - 1) & item; //计算物理页号,即取item的bit0-54
    printf("物理页号为%u\n", item);
    *paddr = (phy_pageIndex * pageSize) + page_offset; //再加上页内偏移量就得到了物理地址
}

const int a = 100; //全局常量
int main()
{
    unsigned long phy = 0; //物理地址

    mem_addr((unsigned long)&a, &phy);
    printf("进程id为 = %d, 虚拟地址为 = %x , 物理地址为 = %x\n", getpid(), &a, phy);
    sleep(10);
    //waitpid();
    return 0;
}

这里写图片描述

3.在windows环境下,编写一个函数(特点:比较耗时),用不同的方法测试其所花费的时间。在不同环境下比较其时间是否不同,并分析其含义。测量时间的函数请baidu。

参考: Windows下利用c语言测试函数运行时间

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <time.h>
#include <windows.h>

void test()
{
    int i = 0;
    while (i < 100000)
    {
        i++;
    }
}

void method_1()
{
    clock_t start = clock();
    test(); //待测试函数
    clock_t end = clock();
    double runtime = (double)(end - start) / CLOCKS_PER_SEC;
    printf("%f", runtime);
}

void method_2()
{
    LARGE_INTEGER BegainTime;
    LARGE_INTEGER EndTime;
    LARGE_INTEGER Frequency;
    QueryPerformanceFrequency(&Frequency);
    QueryPerformanceCounter(&BegainTime);
    test(); //待测试函数
    QueryPerformanceCounter(&EndTime);
    double runtime = (double)(EndTime.QuadPart - BegainTime.QuadPart) / Frequency.QuadPart;

    printf("%f", runtime);
}

int main()
{
    method_1();
    printf("\n");
    Sleep(1000);
    method_2();
    getchar();
}

这里写图片描述

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值