📄 page.c
字号:
/*
* page.c
* Copyright (c) Inst. of Machine Intelligence at Nankai University
*/
#include "page.h"
#include <stdlib.h>
struct Page pages[PAGE_NUM]; //实际上,这个数组是在内核空间,也是要占用物理页的,为简单起见,就不要求写在memory里了
struct page_list free_page_list;
extern int mem_end;
extern char memory[];
extern phyaddr_t cpu_cr3;
extern phyaddr_t cpu_cr2;
extern phyaddr_t cpu_cr0;
extern phyaddr_t cpu_errcode;
int page_init()
{
// 初始化空闲页列表
// 比如,让最后一个物理页最先被分配
// memory中有哪些物理页可以被使用?哪些不能?
int i;
free_page_list.head=NULL;
for(i=0;i<PAGE_NUM;i++)
{
pages[i].ref = 0; //先将所有的页的引用置为0;
if(i*PGSIZE<mem_end) //如果所引用的页在memory数组中未被使用的第一个字节的位置之前
pages[i].ref = 1; //由于存储了页表,故将页的引用为1;
else
{
pages[i].next=free_page_list.head; //通过next指针连接各个页
free_page_list.head=&pages[i]; //空闲队列的队头指向最后一页
}
}
return 0;
}
struct Page* page_alloc()
{
//从空闲页列表中分配一个页, 并返回页面指针
struct Page* temp=free_page_list.head;
if(temp==NULL) //如果没有空闲页了,则退出程序
{
printf("There are no more pages free!\n");
exit(1);
}
else
free_page_list.head=(free_page_list.head)->next; //空闲队列的头指针指向下一个page
return temp;
}
int page_insert(viraddr_t va, struct Page* pp)
{
//将虚拟地址映射到物理页pp上
pde_t* pgdir;
pte_t* pgtable = 0;
pde_t pde = 0;
pte_t pte = 0;
struct Page * get_pgt;
if ((cpu_cr0 & PAGING) != PAGING) //在main函数中寄存器cpu_cr0=PAGING;已经开启了分页
{
//如果未分页的话,退出程序
printf("Error : Paging mode doesn't exist\n");
exit(0);
}
else
{
pgdir = (pde_t*)(memory + cpu_cr3); //pgdir指针指向顶级页表的初始位置
pde = pgdir[PDX(va)]; //取出顶级页表的第PDX(va)项
if ((pde & PTE_P) != PTE_P) //如果顶级页表指向的二级页表不存在则需要重新给其分配
{
get_pgt=page_alloc(); //把即将插入的页的下一个空闲页取出用以存二级页表
pgdir[PDX(va)] = page2pa(get_pgt) | PTE_P ; //最低位或上1,表示新插入的页“在”内存中,同时把新取出的二级页表的地址写入顶级页表
if (cpu_errcode==0x02) //若为只读页时cpu_errcode为0,写时cpu_errcode为2
pgdir[PDX(va)] = pgdir[PDX(va)] | PTE_W ; //此时该顶级页表项后12位等于3,即可写且存在
get_pgt->ref ++; //该页被引用
pde = pgdir[PDX(va)]; //取出重置后的新顶级页表项
}
pgtable = (pde_t*)(memory + PTE_ADDR(pde));
pte = pgtable[PTX(va)];
if ((pte & PTE_P) != PTE_P)
{
pgtable[PTX(va)] = page2pa(pp) | PTE_P ;//最低位或上1,表示新插入的页“在”内存中
if (cpu_errcode==0x02) //若要求可写
pgtable[PTX(va)]=pgtable[PTX(va)] | PTE_W ; //末两位为 11
pp->ref ++;
return 0; //完成虚拟地址映射到物理页,函数返回,不执行下面的操作
}
if( page2pa(pp) != PTE_ADDR(pte) )
{
page_remove(va);
pgtable[PTX(va)] = page2pa(pp) | PTE_P ;
pp->ref ++;
}
return 0;
}
}
int page_remove(viraddr_t va)
{
//移除虚拟页
//注意,什么时候只是取消映射,什么时候要把页重新放回到空闲页列表中?
pde_t* pgdir;
pte_t* pgtable = 0;
pde_t pde = 0;
pte_t pte = 0;
struct Page* page_temp=NULL;
if ((cpu_cr0 & PAGING) != PAGING)
{
//如果未分页,则直接退出
printf("Error : Paging mode doesn't exist\n");
exit(0);
}
else
{
pgdir = (pde_t*)(memory + cpu_cr3);
pde = pgdir[PDX(va)]; //取出虚拟地址va对应顶级页表的页表项内容
if ((pde & PTE_P) != PTE_P) //若该页本身就不存在,即没有虚拟地址映射,则无法执行移除操作,直接提示后退出
{
printf("Error: Page Directory is invalid.\n");
exit(1);
}
pgtable = (pde_t*)(memory + PTE_ADDR(pde));
pte = pgtable[PTX(va)]; //取出虚拟地址va对应二级页表的页表项内容
page_temp=pa2page(PTE_ADDR(pte)); //确定该虚拟地址对应的是哪一页,用page_temp指向它
page_temp->ref--; //取消该页的引用,使其重新变为自由页
if((page_temp->ref)==0)
{
page_temp->next=free_page_list.head;
free_page_list.head=page_temp; //将释放的页插入到空闲队列的队头
}
}
return 0;
}
phyaddr_t page2pa(struct Page* pp)
{
uint32 idx = pp - pages; //idx标识pp为内存中的第几页
phyaddr_t rtv;
rtv = idx * PGSIZE; //获得pp页在内存中的位移
return rtv;
}
struct Page* pa2page(phyaddr_t pa)
{
struct Page* pp = NULL;
pa = ROUNDDOWN(pa, PGSIZE); //化零取整,使得pa是页大小(4096 B)的整数倍
return (pages + (pa / PGSIZE)); //返回pa地址对应的页号
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -