如我在上一篇博文动态分区算法实现中讲到的,這次实现的是离散分配方式里的请求分页算法,涉及到虚拟存储。
我一开始在做這个实验的时候有点无从下手,动态分区好理解吧,你需要多少地,我就给你划分多少地,地不要了,我就回收。但这个请求分页,是个什么鬼?分页就分页吧,把实存平均分,把辅存平均分,每个进程配一个页表,记录一个用到哪一个页对应哪一个块,对应什么就分什么呗,這个页面置换是干什么用的?被困于动态分区的简单思维里,我陷入迷茫之中,眼神中略带倦意。于是,我好好地了解了一下请求分页的原理。
我之前的理解,实际上是基本分页方式,是要将程序全部装入内存中。程序不必连续存放。
一个逻辑地址的组成是页号和页内地址,页号为逻辑地址除页面大小后取整,页内地址为逻辑地址mod页面大小后的值。逻辑地址转换为物理地址的过程就是,将进程中的页号取出,在页表中查询到页号对应的物理块号,用物理块号替代原先的也号,页内地址原位copy。
基本分页的流程就清楚了,进程的页表中记录着所有的页号,对应的块号也一查就出来了,实现so easy。
于是问题来着,基本分页与请求分页的区别呢?
基本分页是实存管理方式,请求分页是虚存管理方式。虚存是是指具有请求调入功能和置换功能, 能从逻辑上对内存容量加以扩充的一种存储器系统。它避免了一次性将程序全部调入内存,并且一直滞留在内存。比如说,在看高清电影的时候,电脑内存大小实际上是小于电影大小的,如果没有虚存,电影会看不了,有了虚存,电影即将播放与正在播放的部分会调入内存,而剩余部分存在辅存,电影這时候就可以正常播放。
涉及到了虚存,就涉及到了程序部分存入内存,就涉及到了如果一个需要访问的一个页面如果恰好不在内存装入的那部分,怎么办?页表中查询不到该页号对应的物理块号,也就无法访问到相应的内容。這时候,就产生了缺页中断,调用页面置换,将需要访问的页面调入内存,页表记录下该页表信息,再进行物理地址的访问。
所以,虚拟存储的方式导致了基本分页与请求分页的不同,部分装入会产生缺页,由此衍生页面置换算法等等。
页面置换算法包括:最佳置换算法,FIFO先进先出算法,LRU最近最久未使用置换算法,clock置换算法(最近未使用算法),改进型clock算法,LFU最少使用置换算法,页面缓冲算法。
此次实验实现的是FIFO先进先出算法。
首先,设置数据结构,设置两种结点,一个结点记录页面所有信息,一个只记录页面id号。设置数组page,记录所有页面id,存在于辅存中的块号,是否调入内存中,对应的内存的物理块号(如果不在内存中,则此项为0)。设置链表,采用队列方式,记录内存中的页号,方便实现先进先出,移动结点。
输入一个进程的所有页面信息,储存在数组中后,根据其中是否调入内存的标志,创建初始队列,先后顺序记录下这些页面号。对于每一个逻辑地址,首先判断是否超出页面逻辑地址上限。将其对应的页号查询page,若不在内存,缺页中断,调用FIFO算法,替代链表中第一位的页面,并插入到队尾,将page数组中相应数据改变,最后计算物理地址。
代码如下:
#include
#include
#define N 64
struct
{
int lNumber; //页号
int pNumber; //物理块号
int dNumber; //在磁盘上的位置
int write; //修改标志
int flag; //存在标志
}page[N];
struct node {
int id;
struct node * link;
node(void) {};
node(int a) { id = a; };
}*start,*first=NULL;
int PageNumber;
int BlockNumer;
void init()
{
printf("输入页表信息,创建页表(若输入为-1,则结束输入):\n");
int n,i=0;
printf("输入第0页的辅存地址:");
scanf("%d", &n);
while (n != -1)
{
page[i].lNumber = i;
page[i].dNumber = n;
page[i].flag = 0;
i++;
printf("\n输入第%d页的辅存地址:",i);
scanf("%d", &n);
}
PageNumber = i;
printf("输入主存块号,块号要小于%d:", PageNumber);
scanf("%d", &n);
i = 0;
while (n != -1)
{
if (i < 4)
{
page[i].pNumber = n;
page[i].flag = 1;
}
scanf("%d", &n);
i++;
}
if (i < 4)
BlockNumer = i;
else
BlockNumer = 4;
for (i = 0; i < PageNumber; i++)
{
printf("\n当前表的内容为:\n");
printf("page[%d].lNumber=%d\t\tpage[%d].pNumber=%d\t\tpage[%d].flag=%d\n", i, page[i].lNumber, i, page[i].pNumber, i, page[i].flag);
}
for (i = 0; i < BlockNumer; i++)
{
if (start == NULL)
first=start = new node(i);
else {
first->link = new node(i);
first = first->link;
}
first->link = NULL;
}
}
int found(int P)
{
int a=0;
node * part;
part = start;
while (part != NULL)
{
if (part->id == P)
a = 1;
part = part->link;
}
return a;
}
void FIFO(int P)
{
printf("访问的%d页不存在,发生缺页中断\n",P);
int before;
node *end = first = start;
while (end->link != NULL)
end = end->link;
start = start->link;
before = first->id;
first->id = P;
first->link = NULL;
end->link = first;
page[P].pNumber = page[before].pNumber;
page[P].flag = 1;
page[before].flag = 0;
page[before].pNumber = 0;
printf("淘汰主存块%d中的页%d,从磁盘第%d块中调入页%d\n", page[P].pNumber, before, page[P].dNumber, P);
}
int main()
{
init();
int swrite, address,is_found;
int P;
int Ad;
int judge = 1;
while (judge == 1)
{
printf("输入指令性质(1-修改,0-不修改)和逻辑地址:");
scanf("%d", &swrite);
scanf("%d", &address);
if (address >= PageNumber * 1024)
printf("地址超出范围\n");
else
{
P = address / 1024;
Ad = address - P * 1024;
is_found = found(P);
while (is_found == 0)
{
FIFO(P);
is_found = found(P);
}
page[P].write = swrite;
printf("逻辑地址:%d 对应的物理地址为:%d \n", address, 1024 * page[P].pNumber + Ad);
}
printf("是否继续(继续1,否0):");
scanf("%d", &judge);
}
return 0;
}