课程设计目标
用代码描述:页表,页框表,任务结构
用代码实现:页请求,页置换
模拟任务的页面访问过程(访问虚拟内存)
模拟操作系统中的任务调度(拓展)
实现的关键点一
页框表是全局唯一的,用于记录内存页使用情况
页表是任务私有的,用于记录虚拟页到内存页的映射关系
每一个任务需要一个结构体进行表示
实现的关键点二
页分配操作:int GetFrameItem();
页请求操作:int RequestPage(int pid, int page);
页交换操作:int SwapPage();
实现的关键点三
如何模拟不同的任务?
- 不同任务的虚拟页访问顺序不同
- 因此,可以随机产生页面访问序列
具体的实现流程
#include <QCoreApplication>
#include <iostream>
#include <QList>
#include <ctime>
using namespace std;
#define PAGE_NUM (0xFF + 1)
#define FRAME_NUM (0x04)
#define FP_NONE (-1)
struct FrameItem
{
int pid; // the task which use the frame
int pnum; // the page which the frame hold
FrameItem()
{
pid = FP_NONE;
pnum = FP_NONE;
}
};
class PageTable
{
private:
int m_pt[PAGE_NUM];
public:
PageTable()
{
for(int i = 0; i < PAGE_NUM; i++)
{
m_pt[i] = FP_NONE;
}
}
int& operator[](int i)
{
if((i >= 0) && (i < length()))
{
return m_pt[i];
}
QCoreApplication::exit(-1);
}
int length() const
{
return PAGE_NUM;
}
};
class PCB
{
private:
int m_pid; // task id
PageTable m_pageTable; // page table for the task
int* m_pageSerial; // simulate the page serial access
int m_pageSerialCount; // page access count
int m_next; // the next page index to access
public:
PCB(int pid)
{
m_pid = pid;
m_pageSerialCount = qrand() % 5 + 5;
m_pageSerial = new int[m_pageSerialCount];
for(int i = 0; i < m_pageSerialCount; i++)
{
m_pageSerial[i] = qrand() % 8;
}
m_next = 0;
}
int getPid()
{
return m_pid;
}
PageTable& getPageTable()
{
return m_pageTable;
}
int getNextPage()
{
int ret = m_next++;
if(ret < m_pageSerialCount)
{
ret = m_pageSerial[ret];
}
else
{
ret = FP_NONE;
}
return ret;
}
bool running()
{
return (m_next < m_pageSerialCount);
}
void printPageSerial()
{
QString s = "";
for(int i = 0; i < m_pageSerialCount; i++)
{
s += QString::number(m_pageSerial[i]) + " ";
}
cout << "Task" << QString::number(m_pid).toStdString() << " : " << s.toStdString() << endl;
}
~PCB()
{
delete[] m_pageSerial;
}
};
FrameItem FrameTable[FRAME_NUM];
QList<PCB*> TaskTable;
int GetFrameItem();
void AccessPage(PCB& pcb);
int RequestPage(int pid, int page);
int SwapPage();
void PrintLog(QString log);
void PrintPageMap(int pid, int page, int frame);
void PrintFatalError(QString s, int pid, int page);
int GetFrameItem()
{
int ret = FP_NONE;
for(int i = 0; i < FRAME_NUM; i++)
{
if(FrameTable[i].pid == FP_NONE)
{
ret = i;
break;
}
}
return ret;
}
void AccessPage(PCB& pcb)
{
int pid = pcb.getPid();
PageTable& pagetable = pcb.getPageTable();
int page = pcb.getNextPage();
if(page != FP_NONE)
{
PrintLog("Access Task" + QString::number(pid) + " for page" + QString::number(page));
if(pagetable[page] != FP_NONE)
{
PrintLog("Find target page in page table.");
PrintPageMap(pid, page, pagetable[page]);
}
else
{
PrintLog("Target page is not found, need to request page ...");
pagetable[page] = RequestPage(pid, page);
if(pagetable[page] != FP_NONE)
{
PrintPageMap(pid, page, pagetable[page]);
}
else
{
PrintFatalError("Can NOT request page from disk ...", pid, page);
}
}
}
else
{
PrintLog("Task" + QString::number(pid) + " is finished!");
}
}
int RequestPage(int pid, int page)
{
int frame = GetFrameItem();
if(frame != FP_NONE)
{
PrintLog("Get a frame to hold page content: Frame" + QString::number(frame));
}
else
{
PrintLog("No free frame to allocate, need to swap page out.");
frame = SwapPage();
if(frame != FP_NONE)
{
PrintLog("Succeed to swap lazy page out.");
}
else
{
PrintFatalError("Failed to swap page out.", pid, FP_NONE);
}
}
PrintLog("Load content from disk to Frame" + QString::number(frame));
FrameTable[frame].pid = pid;
FrameTable[frame].pnum = page;
return frame;
}
int Random()
{
// just random select
int obj = qrand() % FRAME_NUM;
PrintLog("Random select a frame to swap page content out: Frame" + QString::number(obj));
PrintLog("Writed the selected page content back to disk.");
FrameTable[obj].pid = FP_NONE;
FrameTable[obj].pnum = FP_NONE;
for(int i = 0, f = 0; (i < TaskTable.count() && !f); i++)
{
PageTable& pt = TaskTable[i]->getPageTable();
for(int j = 0; j < pt.length(); j++)
{
if(pt[j] == obj)
{
f = 1;
pt[j] = FP_NONE;
break;
}
}
}
return obj;
}
int SwapPage()
{
return Random();
}
void RecycleTask(PCB& pcb)
{
PrintLog("Task" + QString::number(pcb.getPid()) + " is finished ...");
PageTable& pt = pcb.getPageTable();
for(int i = 0; i < pt.length(); i++)
{
if(pt[i] != FP_NONE)
{
FrameTable[pt[i]].pid = FP_NONE;
FrameTable[pt[i]].pnum = FP_NONE;
PrintLog("Remove Frame" + QString::number(pt[i]));
pt[i] = FP_NONE;
}
}
}
void PrintLog(QString log)
{
cout << log.toStdString() << endl;
}
void PrintPageMap(int pid, int page, int frame)
{
QString s = "";
s += "Task" + QString::number(pid) + " : " + "Page" + QString::number(page) + " ==> " + "Frame" + QString::number(frame);
cout << s.toStdString() << endl;
}
void PrintFatalError(QString s, int pid, int page)
{
s += "Task" + QString::number(pid) + " : " + "Page" + QString::number(page);
cout << s.toStdString() << endl;
QCoreApplication::exit(-2);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int index = 0;
qsrand(time(NULL));
TaskTable.append(new PCB(1));
TaskTable.append(new PCB(2));
PrintLog("Task Page Serial:");
for(int i = 0; i < TaskTable.count(); i++)
{
TaskTable[i]->printPageSerial();
}
PrintLog("=======Running=======");
while(true)
{
if(TaskTable.count() > 0)
{
if(TaskTable[index]->running())
{
AccessPage(*TaskTable[index]);
}
else
{
RecycleTask(*TaskTable[index]);
}
}
if(TaskTable.count() > 0)
{
index = qrand() % TaskTable.count();
}
cin.get();
}
return a.exec();
}
8-10行定义了三个常量,我们把一个任务的最多的页表数量定义为256,页框的最大数量定义为4,FP_NONE表明这个页表号或者页框号未被使用。
12行-22行定义了页框项,它有两个成员变量,一个是拥有此页框的任务id,另一个是与此页框相关联的页表。
24-52行定义了页表,它里面定义了从页表号到页框号的映射,这里采用的是单级映射。
54-125行定义了任务控制块,有5个成员变量,第一个为任务的id号,第二个为页表中的页号,第三个为需要访问的页表号的数组,第四个为需要访问页表号的数量,第五个为下一个需要访问的页表号。在这里,我们设计得比较简单,需要访问页表号的数量和页表值都是随机产生的。
127行,我们定义了页框表,128行,我们定义了一个链表来存储任务控制块。
138行这个函数的作用:用来返回空闲的页框号。
154行这个函数的作用:模拟一次页访问。
189行这个函数的作用:完成一次页请求。
221行这个函数的作用:当前的页框已经满了,我们随机产生一个index,把FrameTable[index]所对应的页框移除。注意:要把清除相对应的页表相关映射信息。
255行这个函数的作用:当一个任务结束时,我们需要清理它所占用的页框项。以供其他任务使用。
在main函数里,我们定义了两个任务,在while循环里,模拟这两个任务的页请求。宏观上,这两个任务是并行执行的。当任务结束时,我们清理该任务对应的页框项。