(1)报告连同源码打包发到 [email protected],文件名:姓名学号班级OS实验报告.rar
(2)邮件主题和附件文件名:姓名学号班级OS实验报告
(3)源代码必须删除debug和release目录!
一、实验目的
此处主要粘贴四次实验指南中实验目的条目的内容。按1),2)…方式顺序编号
二、实验内容
此处主要粘贴四次实验指南中实验内容条目的内容。按1),2)…方式顺序编号
三、实验过程
此处主要粘贴核心的关键源代码和程序流程,并分析。把你做完了的全部编程题按1),2)…方式顺序编号,每个编程题目自己给取一个5-10字的名字作为标题。
四、实验结果
此处主要粘贴运行截图和分析。把你做完了的全部编程题按1),2)…方式顺序编号,每个编程题目自己给取一个5-10字的名字作为标题。
五、体会
写体会
一. 实验目的:
- 理解页面淘汰算法原理,编写程序演示页面淘汰算法。
- 验证Linux虚拟地址转化为物理地址的机制
- 理解和验证程序运行局部性的原理。
二. 实验内容:
1.在windows环境下编写一个程序,模拟实现OPT,FIFO,LRU等页面淘汰算法。具体实验思想和过程请参考《内存管理实验指导书》。
参考:
法一:
#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。
#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();
}