1.论述
1.1.获取计算机系统所有实际可用物理区域信息
计算机系统的物理区域并不是连续的,且有的物理区域映射到特定硬件内部区域以便实现特定功能。
对计算机系统的物理区域进行分页管理,首先,要获取所有离散的物理区域信息。
INT 15h, AX=E820h
调用传入:
EAX=0000E820h
EDX=534D4150h
EBX=00000000(此值为起始映射结构体,其他值为后续映射结构体)
ECX=预设返回结果的缓存区结构体长度,以字节为单位(应该大于等于20B)
ES:DI=保存返回结果的缓存区地址
调用返回:
如果CF=0,则说明操作执行成功。
EAX=534D4150h(字符串"SMAP")
ES:DI=保存返回结果的缓存区地址
EBX=00000000h(此值表示检测结束,其他值为后续映射信息结构体序号)
ECX=保存实际操作的缓存区结构体长度,以字节为单位
如果CF=1,则说明操作执行失败
AH=错误码(80h:无效命令;86h:不支持此功能)
物理地址空间信息结构
偏移值 | 字节数 | 描述 |
00h | 8B | 起始地址 |
08h | 8B | 长度(以字节为单位) |
10h | 4B | 内存类型 |
内存类型介绍表
类型值 | 描述 |
01h | 可用物理内存 |
02h | 保留或无效值(包括ROM,设备内存) |
03h | ACPI的回收内存 |
04h | ACPINVS内存 |
其他值 | 未定义,保留使用 |
2.实践
1.1.获取计算机系统所有物理区域信息,并将信息连续存储到值定物理位置
; 这部分汇编采用的是Intel汇编格式
mov ebx, 0
mov ax, 0x00
mov es, ax
mov di, 0x7E00
Label_Get_Mem_Struct:
mov eax, 0x0E820
mov ecx, 20
mov edx, 0x534D4150
int 15h
jc Label_Get_Mem_Fail
add di, 20
cmp ebx, 0
jne Label_Get_Mem_Struct
jmp Label_Get_Mem_OK
上述指令的作用是,从物理位置0x7E00开始连续存储多个20字节的物理地址空间信息结构。
1.2.通过一个全局的对象实例进行物理页管理
struct Global_Memory_Descriptor
{
struct E820 e820[32];
unsigned long e820_length;
unsigned long * bits_map;
unsigned long bits_size;
unsigned long bits_length;
struct Page * pages_struct;
unsigned long pages_size;
unsigned long pages_length;
struct Zone * zones_struct;
unsigned long zones_size;
unsigned long zones_length;
unsigned long start_code , end_code , end_data , end_brk;
unsigned long end_of_struct;
};
// 1.对象实例初始化
struct Global_Memory_Descriptor memory_management_struct = {{0},0};
1.3.为memory_management_struct 进行有意义的初始化
void init_memory()
{
// 1.为实例对象的下述字段赋值
// start_code表示内核代码段起始位置线性地址
memory_management_struct.start_code = (unsigned long)& _text;
// end_code表示内核代码段尾后位置线性地址
memory_management_struct.end_code = (unsigned long)& _etext;
// end_data表示内核数据段尾后位置线性地址
memory_management_struct.end_data = (unsigned long)& _edata;
// end_brk是一个动态变化值,表示当前可供分配区域起始线性地址
memory_management_struct.end_brk = (unsigned long)& _end;
int i,j;
unsigned long TotalMem = 0 ;
struct E820 *p = NULL;
color_printk(BLUE,BLACK,"Display Physics Address MAP,Type(1:RAM,2:ROM or Reserved,3:ACPI Reclaim Memory,4:ACPI NVS Memory,Others:Undefine)\n");
p = (struct E820 *)0xffff800000007e00;
// 2.从7e00物理内存位置依次取出若干个20字节。并依据20字节内容设置实例对象的e820数组元素。
// 这里假定得到物理区域数量不会超过32个。
for(i = 0; i < 32; i++)
{
if(p->type == 1)
color_printk(ORANGE,BLACK,"Address:%#018lx\tLength:%#018lx\tType:%#010x\n",p->address,p->length,p->type);
if(p->type == 1)
TotalMem += p->length;
memory_management_struct.e820[i].address = p->address;
memory_management_struct.e820[i].length = p->length;
memory_management_struct.e820[i].type = p->type;
// 实例对象e820_length表示实际拥有的物理区域数量。这个数值随着遍历来统计出来的。
memory_management_struct.e820_length = i;
p++;
if(p->type > 4 || p->length == 0 || p->type < 1)
break;
}
TotalMem = 0;
// 3.这里无实际功能上的意义。目的是遍历所有物理区域,统计出来这些物理区域中存在多少可被分配的2MB物理页
// 对于每个可供分配的2MB物理页要求物理页基地址必须按2MB对齐。
for(i = 0; i <= memory_management_struct.e820_length; i++)
{
unsigned long start,end;
if(memory_management_struct.e820[i].type != 1)
continue;
start = PAGE_2M_ALIGN(memory_management_struct.e820[i].address);
end = ((memory_management_struct.e820[i].address + memory_management_struct.e820[i].length) >> PAGE_2M_SHIFT) << PAGE_2M_SHIFT;
if(end <= start)
continue;
TotalMem += (end - start) >> PAGE_2M_SHIFT;
}
color_printk(ORANGE,BLACK,"OS Can Used Total 2M PAGEs:%#018lx=%018ld\n",TotalMem,TotalMem);
// 4.实例对象通过引入bit_map来加快物理页分配和回收的速度。
// bits_map按64比特位作为一组。每个比特位代表一个2MB物理页。
//bits map construction init
// 这里假定最后一个物理区域的起始地址是所有区域中最大的。所以,TotalMem就是物理区域最大可达物理地址
TotalMem = memory_management_struct.e820[memory_management_struct.e820_length].address
+ memory_management_struct.e820[memory_management_struct.e820_length].length;
// 实例对象的end_brk是当前可供分配区域起始线性地址。
// 从此起始开始寻找首个可以对齐到4KB的线性地址作为bits_map起始存储位置
memory_management_struct.bits_map =
(unsigned long *)((memory_management_struct.end_brk + PAGE_4K_SIZE - 1) & PAGE_4K_MASK);
// bits_map中需要管理的比特位个数计算,这些数量的比特位包含了所有可用2MB物理页
memory_management_struct.bits_size = TotalMem >> PAGE_2M_SHIFT;
// bits_map部分占据尺寸,一方面需要能容纳指定数量比特位,另一方面,这个尺寸需要是8字节对齐的。
memory_management_struct.bits_length =
(((unsigned long)(TotalMem >> PAGE_2M_SHIFT) + sizeof(long) * 8 - 1) / 8) & ( ~ (sizeof(long) - 1));
// bits_map中管理的所有比特位设置为1,这样表示所有物理页都不可被分配
memset(memory_management_struct.bits_map,0xff,memory_management_struct.bits_length);
// 5.实例对象通过引入pages_struct来对每个物理页进行管理
//pages construction init
// pages_struct区域起始线性地址是bits_map区域后首个可以与4KB对齐的线性地址
memory_management_struct.pages_struct =
(struct Page *)(((unsigned long)memory_management_struct.bits_map + memory_management_struct.bits_length
+ PAGE_4K_SIZE - 1) & PAGE_4K_MASK);
// pages_structs中需要管理的物理页数量
memory_management_struct.pages_size = TotalMem >> PAGE_2M_SHIFT;
// pages_struct部分占据尺寸,一方面需要能容纳指定数量的Page对象,另一方面,这个尺寸需要是8字节对齐的。
memory_management_struct.pages_length =
((TotalMem >> PAGE_2M_SHIFT) * sizeof(struct Page) + sizeof(long) - 1) & ( ~ (sizeof(long) - 1));
// 对每个Page对象内存置空来完成初始化
memset(memory_management_struct.pages_struct,0x00,memory_management_struct.pages_length);
// 6.实例对象通过引入zones_struct来对每个物理区域进行管理。
//zones construction init
// zones_struct区域起始线性地址是pages_struct区域后首个可以与4KB对齐的线性地址
memory_management_struct.zones_struct =
(struct Zone *)(((unsigned long)memory_management_struct.pages_struct + memory_management_struct.pages_length + PAGE_4K_SIZE - 1) & PAGE_4K_MASK);
// 所需管理的物理区域数量暂时未知
memory_management_struct.zones_size = 0;
// 先假定至多只有10个物理区域需要管理。
// zones_struct部分占据尺寸,一方面需要能容纳指定数量的Zone对象,另一方面,这个尺寸需要是8字节对齐的。
memory_management_struct.zones_length = (10 * sizeof(struct Zone) + sizeof(long) - 1) & (~(sizeof(long) - 1));
// 对每个Zone对象内存置空来完成初始化
memset(memory_management_struct.zones_struct,0x00,memory_management_struct.zones_length);
// 7.对原始收集的物理区域进行分析来得到需要被管理的物理区域。并为其构造Zone对象。
for(i = 0; i <= memory_management_struct.e820_length; i++)
{
unsigned long start,end;
struct Zone * z;
struct Page * p;
// 可以被管理的物理区域需要满足条件
// 1.物理区域的类型需要是1--可分配内存
// 2.物理区域内部至少有一个基地址2MB对齐的物理页可供使用。
if(memory_management_struct.e820[i].type != 1)
continue;
start = PAGE_2M_ALIGN(memory_management_struct.e820[i].address);
end = ((memory_management_struct.e820[i].address + memory_management_struct.e820[i].length) >> PAGE_2M_SHIFT) << PAGE_2M_SHIFT;
if(end <= start)
continue;
// 对可被管理的物理区域。为其分配Zone对象。
// 这里找到该区域关联的Zone对象线性地址
z = memory_management_struct.zones_struct + memory_management_struct.zones_size;
// 更新实例对象zones_size字段
memory_management_struct.zones_size++;
// 对Zone对象进行符合物理区域实际的初始化
// 区域起始物理地址(已经按2MB对齐)
z->zone_start_address = start;
// 区域尾后物理地址(已经按2MB对齐)
z->zone_end_address = end;
// 尺寸。这是区域内可分配物理页尺寸和
z->zone_length = end - start;
// 区域内物理页中被使用页数量
z->page_using_count = 0;
// 区域内物理页中可供分配页数量
z->page_free_count = (end - start) >> PAGE_2M_SHIFT;
z->total_pages_link = 0;
// 区域属性
z->attribute = 0;
// Zone隶属的实例对象地址
z->GMD_struct = &memory_management_struct;
// Zone内可供分配物理页数量
z->pages_length = (end - start) >> PAGE_2M_SHIFT;
// Zone内首个可供分配物理页关联的Page对象线性地址
z->pages_group = (struct Page *)(memory_management_struct.pages_struct + (start >> PAGE_2M_SHIFT));
// 对区域内可供分配的每个Page对象进行和实际情况相符的初始化
p = z->pages_group;
for(j = 0; j < z->pages_length; j++, p++)
{
// 设置Page对象隶属的Zone对象地址
p->zone_struct = z;
// 设置Page对象代表的物理页的基地址
p->PHY_address = start + PAGE_2M_SIZE * j;
// 设置Page对象属性
p->attribute = 0;
// 设置Page对象引用数
p->reference_count = 0;
// 设置Page对象age
p->age = 0;
// 找到Page对象在bits_map中的比特位,并将此比特位设置为0
// 比特位为0表示此比特位代表的物理页可被分配
*(memory_management_struct.bits_map + ((p->PHY_address >> PAGE_2M_SHIFT) >> 6)) ^= 1UL << (p->PHY_address >> PAGE_2M_SHIFT) % 64;
}
}
// 8.根据实际管理的Zone对象数量,计算zones_struct占据尺寸。
// 尺寸需要能容纳指定数量的Zone对象,且需满足8字节对齐。
memory_management_struct.zones_length = (memory_management_struct.zones_size * sizeof(struct Zone) + sizeof(long) - 1) & ( ~ (sizeof(long) - 1));
//color_printk(ORANGE,BLACK,"bits_map:%#018lx,bits_size:%#018lx,bits_length:%#018lx\n",memory_management_struct.bits_map,memory_management_struct.bits_size,memory_management_struct.bits_length);
//color_printk(ORANGE,BLACK,"pages_struct:%#018lx,pages_size:%#018lx,pages_length:%#018lx\n",memory_management_struct.pages_struct,memory_management_struct.pages_size,memory_management_struct.pages_length);
//color_printk(ORANGE,BLACK,"zones_struct:%#018lx,zones_size:%#018lx,zones_length:%#018lx\n",memory_management_struct.zones_struct,memory_management_struct.zones_size,memory_management_struct.zones_length);
// 9.对区域分类。区域起始物理地址低于4GB的属于一类。高于或等于4GB的属于一类。
ZONE_DMA_INDEX = 0;
ZONE_NORMAL_INDEX = 0;
ZONE_UNMAPED_INDEX = 0;
for(i = 0; i < memory_management_struct.zones_size; i++)
{
struct Zone * z = memory_management_struct.zones_struct + i;
//color_printk(ORANGE,BLACK,"zone_start_address:%#018lx,zone_end_address:%#018lx,zone_length:%#018lx,pages_group:%#018lx,pages_length:%#018lx\n",z->zone_start_address,z->zone_end_address,z->zone_length,z->pages_group,z->pages_length);
if(z->zone_start_address >= 0x100000000 && !ZONE_UNMAPED_INDEX)
ZONE_UNMAPED_INDEX = i;
}
//color_printk(ORANGE,BLACK,"ZONE_DMA_INDEX:%d\tZONE_NORMAL_INDEX:%d\tZONE_UNMAPED_INDEX:%d\n",ZONE_DMA_INDEX,ZONE_NORMAL_INDEX,ZONE_UNMAPED_INDEX);
// 10.重新计算当前可供分配区域起始线性地址
// 因为在用的物理区域除了内核固有的,前面处理中还向前扩展出了bits_map,pages_struct,zones_struct区域
// 所以,在zones_struct区域后,再隔开一点空间,找一个8字节对齐的线性地址作为后续可用区域起始线性地址。
memory_management_struct.end_of_struct = (unsigned long)((unsigned long)memory_management_struct.zones_struct + memory_management_struct.zones_length + sizeof(long) * 32) & ( ~ (sizeof(long) - 1)); need a blank to separate memory_management_struct
//color_printk(ORANGE,BLACK,"start_code:%#018lx,end_code:%#018lx,end_data:%#018lx,end_brk:%#018lx,end_of_struct:%#018lx\n",memory_management_struct.start_code,memory_management_struct.end_code,memory_management_struct.end_data,memory_management_struct.end_brk, memory_management_struct.end_of_struct);
i = Virt_To_Phy(memory_management_struct.end_of_struct) >> PAGE_2M_SHIFT;
// 对我们目前已经使用的部分。分解到每个Page对象。对这些Page对象进行相应设置来反映它们的被使用情况。
for(j = 0; j <= i; j++)
{
struct Page * tmp_page = memory_management_struct.pages_struct + j;
// 设置Page对象属性
page_init(tmp_page, PG_PTable_Maped | PG_Kernel_Init | PG_Kernel);
// 将Page对象对应物理页所对应的bits_map中比特位设置为1。表示物理页已被使用。
*(memory_management_struct.bits_map + ((tmp_page->PHY_address >> PAGE_2M_SHIFT) >> 6)) |= 1UL << (tmp_page->PHY_address >> PAGE_2M_SHIFT) % 64;
// 这里做这个判断的原因是,在物理内存区域,尤其是前面部分。常常存在一个或多个不足2MB小区域。
// 因为我们按2MB来管理物理页。所以物理区域[0,2MB)本来因为由离散的多个小区域组成,是无法被我们管理的。
// 但我们确实在boot,loader,kernel处理中均使用了这块区域。所以,我们对其特殊处理。
// 既这样的物理页没有关联的zone,但关联的Page对象依然被设置来物理页的使用情况。
if(tmp_page->zone_struct == NULL)
{
tmp_page->zone_struct = NULL;
tmp_page->PHY_address = j << PAGE_2M_SHIFT;
tmp_page->reference_count = 1;
tmp_page->age = 0;
}
else
{
tmp_page->zone_struct->page_using_count++;
tmp_page->zone_struct->page_free_count--;
}
}
//Global_CR3 = Get_gdt();
//color_printk(INDIGO,BLACK,"Global_CR3\t:%#018lx\n",Global_CR3);
//color_printk(INDIGO,BLACK,"*Global_CR3\t:%#018lx\n",*Phy_To_Virt(Global_CR3) & (~0xff));
//color_printk(PURPLE,BLACK,"**Global_CR3\t:%#018lx\n",*Phy_To_Virt(*Phy_To_Virt(Global_CR3) & (~0xff)) & (~0xff));
//color_printk(ORANGE,BLACK,"1.memory_management_struct.bits_map:%#018lx\tzone_struct->page_using_count:%d\tzone_struct->page_free_count:%d\n",*memory_management_struct.bits_map,memory_management_struct.zones_struct->page_using_count,memory_management_struct.zones_struct->page_free_count);
//flush_tlb();
}
上述过程展开具体介绍可以参考注释部分。
1.4.执行到这里我们为memory_management_struct实例对象进行了有意义的初始化。后续物理页管理将借助memory_management_struct来完成。
但是我们接下来继续再做一件事。
当我们要访问一个虚拟地址时,系统得通过页表,得到相应得物理地址,再基于物理地址进行实际得访问工作。
前面我们初始化memory_management_struct对象得过程中,处理了每个可以分配物理页得物理区域。如果想我们后续分配出去得物理页,可以直接基于线性地址进行访问。我们还需为线性地址能转换为物理地址进行页表注册工作。
void pagetable_init()
{
unsigned long i,j;
unsigned long * tmp = NULL;
// 1.获得CR3寄存器值
Global_CR3 = Get_gdt();
// 从CR3得到PML4页表物理基地址
// 页表物理基地址+8*256得到索引为256得表项得地址
tmp = (unsigned long *)(((unsigned long)Phy_To_Virt((unsigned long)Global_CR3 & (~ 0xfffUL))) + 8 * 256);
// color_printk(YELLOW,BLACK,"1:%#018lx,%#018lx\t\t\n",(unsigned long)tmp,*tmp);
// 通过表项内容得到PDPT页表得物理基地址
tmp = Phy_To_Virt(*tmp & (~0xfffUL));
// color_printk(YELLOW,BLACK,"2:%#018lx,%#018lx\t\t\n",(unsigned long)tmp,*tmp);
// 取得PDPT索引0表项内容,通过表项内容得到PDT页表物理基地址
tmp = Phy_To_Virt(*tmp & (~0xfffUL));
// color_printk(YELLOW,BLACK,"3:%#018lx,%#018lx\t\t\n",(unsigned long)tmp,*tmp);
// 2.对我们所管理得每个区域
// 这样使得我们实例对象里每个zone下可供分配得page对象,
// 一旦被分配出去后,可以直接拿到此page对应物理页得虚拟地址进行地址访问,无需再进行页表注册
for(i = 0; i < memory_management_struct.zones_size; i++)
{
struct Zone * z = memory_management_struct.zones_struct + i;
struct Page * p = z->pages_group;
// 如果存在起始地址超过或等于4GB得物理区域,这类区域不需一开始就进行页表注册。
if(ZONE_UNMAPED_INDEX && i == ZONE_UNMAPED_INDEX)
break;
// 对区域内每个物理页
for(j = 0; j < z->pages_length; j++,p++)
{
// 从物理页Page对象得到物理页基地址
// 通过物理页基地址得到线性地址
// 通过线性地址拆分出该地址在PML4页表得索引
// 结合CR3得到PML4页表物理基地址
// 得到PML4页表项线性地址
tmp = (unsigned long *)(((unsigned long)Phy_To_Virt((unsigned long)Global_CR3 & (~ 0xfffUL))) + (((unsigned long)Phy_To_Virt(p->PHY_address) >> PAGE_GDT_SHIFT) & 0x1ff) * 8);
// 取得PML4页表项内容
if(*tmp == 0)
{
// 动态分配4KB区域
unsigned long * virtual = kmalloc(PAGE_4K_SIZE,0);
// 将此4KB区域作为PDPT表
// 据此设置PML4页表项内容
set_mpl4t(tmp, mk_mpl4t(Virt_To_Phy(virtual), PAGE_KERNEL_GDT));
}
// 从物理页Page对象得到物理页基地址
// 通过物理页基地址得到线性地址
// 通过线性地址拆分出该地址在PDPT页表得索引
// 结合PDPT页表物理基地址
// 取得PDPT页表项内容
tmp = (unsigned long *)((unsigned long)Phy_To_Virt(*tmp & (~ 0xfffUL)) + (((unsigned long)Phy_To_Virt(p->PHY_address) >> PAGE_1G_SHIFT) & 0x1ff) * 8);
if(*tmp == 0)
{
// 动态分配4KB区域
unsigned long * virtual = kmalloc(PAGE_4K_SIZE,0);
// 将此4KB区域作为PDT表
// 据此设置PDPT页表项内容
set_pdpt(tmp, mk_pdpt(Virt_To_Phy(virtual), PAGE_KERNEL_Dir));
}
// 从物理页Page对象得到物理页基地址
// 通过物理页基地址得到线性地址
// 通过线性地址拆分出该地址在PDT页表得索引
// 结合PDT页表物理基地址
// 得到PDT页表项内容
tmp = (unsigned long *)((unsigned long)Phy_To_Virt(*tmp & (~ 0xfffUL)) + (((unsigned long)Phy_To_Virt(p->PHY_address) >> PAGE_2M_SHIFT) & 0x1ff) * 8);
// 设置PDT页表项内容
set_pdt(tmp,mk_pdt(p->PHY_address,PAGE_KERNEL_Page));
}
}
// 一旦对多级页表中得一级或多级进行了修改后,需要执行flush_tlb()
// 使得缓存得页表项失效
flush_tlb();
}
通过上述处理可以使得我们实例对象里每个zone下可供分配得page对象,
一旦被分配出去后,可以直接拿到此page对应物理页得虚拟地址进行地址访问,无需再进行页表注册。
我们实例对象里每个zone下可供分配得page对象还有一个特点是,这些page所代表得物理页中,虚拟地址和物理地址是如下换算关系。
#define PAGE_OFFSET ((unsigned long)0xffff800000000000)
#define Phy_To_Virt(addr) ((unsigned long *)((unsigned long)(addr) + PAGE_OFFSET))
1.5.下面来实现物理页分配
// 参数1:指定区域
// 参数2:页面数量
// 参数3:标志信息
// 这里分配出来的Page,通过Page得到物理页基地址,再得到物理页线性地址。
// 这样得到的线性地址是可以直接访问使用的。因为在此之前,我们已经为我们管理的zone下的每个page均进行了页表注册。
struct Page * alloc_pages(int zone_select,int number,unsigned long page_flags)
{
int i;
unsigned long page = 0;
unsigned long attribute = 0;
int zone_start = 0;
int zone_end = 0;
// 对一次性分配的物理页数量做了限制。最多一次分配63个连续物理页。
if(number >= 64 || number <= 0)
{
color_printk(RED,BLACK,"alloc_pages() ERROR: number is invalid\n");
return NULL;
}
switch(zone_select)
{
case ZONE_DMA:
zone_start = 0;
zone_end = ZONE_DMA_INDEX;
attribute = PG_PTable_Maped;
break;
case ZONE_NORMAL:
zone_start = ZONE_DMA_INDEX;
zone_end = ZONE_NORMAL_INDEX;
attribute = PG_PTable_Maped;
break;
case ZONE_UNMAPED:
zone_start = ZONE_UNMAPED_INDEX;
zone_end = memory_management_struct.zones_size - 1;
attribute = 0;
break;
default:
color_printk(RED,BLACK,"alloc_pages() ERROR: zone_select index is invalid\n");
return NULL;
break;
}
color_printk(RED,BLACK,"alloc_pages() area_min:%#018lx area_max:%#018lx\n", zone_start, zone_end);
for(i = zone_start; i <= zone_end; i++)
{
struct Zone * z;
unsigned long j;
unsigned long start,end;
unsigned long tmp;
// 对zone进行刷选
if((memory_management_struct.zones_struct + i)->page_free_count < number)
continue;
z = memory_management_struct.zones_struct + i;
start = z->zone_start_address >> PAGE_2M_SHIFT;
end = z->zone_end_address >> PAGE_2M_SHIFT;
tmp = 64 - start % 64;
// 对zone内每个page借助bits_map快速定位
for(j = start; j < end; j += j % 64 ? tmp : 64)
{
unsigned long * p = memory_management_struct.bits_map + (j >> 6);
unsigned long k = 0;
unsigned long shift = j % 64;
unsigned long num = (1UL << number) - 1;
// 从page对应的比特位开始
for(k = shift; k < 64; k++)
{
// 一次性检测出从page开始是否存在连续数量的空闲比特位
if( !( (k ? ((*p >> k) | (*(p + 1) << (64 - k))) : *p) & (num) ) )
{
unsigned long l;
page = j + k - shift;
for(l = 0;l < number;l++)
{
struct Page * pageptr = memory_management_struct.pages_struct + page + l;
// 分配出去的Page对应的比特位设置为1
*(memory_management_struct.bits_map + ((pageptr->PHY_address >> PAGE_2M_SHIFT) >> 6)) |= 1UL << (pageptr->PHY_address >> PAGE_2M_SHIFT) % 64;
// 隶属zone字段更新
z->page_using_count++;
z->page_free_count--;
// page属性设置
pageptr->attribute = attribute;
}
goto find_free_pages;
}
}
}
}
// 遭遇到了无法分配场景
color_printk(RED,BLACK,"alloc_pages() ERROR: no page can alloc\n");
return NULL;
find_free_pages:
return (struct Page *)(memory_management_struct.pages_struct + page);
}
这里分配出来的Page,通过Page得到物理页基地址,再得到物理页线性地址。
这样得到的线性地址是可以直接访问使用的。
因为在此之前,我们已经为我们管理的zone下的每个page均进行了页表注册。
如果页表管理4GB区域,采用2MB物理页,其实对所有4GB区域进行页表注册,只需一个PML4页表,1个PDPT页表,4个PDT页表即可。
1.6.下面实现物理页释放
void free_pages(struct Page * page,int number)
{
int i = 0;
if(page == NULL)
{
color_printk(RED,BLACK,"free_pages() ERROR: page is invalid\n");
return ;
}
if(number >= 64 || number <= 0)
{
color_printk(RED,BLACK,"free_pages() ERROR: number is invalid\n");
return ;
}
for(i = 0;i<number;i++,page++)
{
// bits_map中对应比特位设置为0
*(memory_management_struct.bits_map + ((page->PHY_address >> PAGE_2M_SHIFT) >> 6)) &= ~(1UL << (page->PHY_address >> PAGE_2M_SHIFT) % 64);
// 所属zone字段设置
page->zone_struct->page_using_count--;
page->zone_struct->page_free_count++;
// 属性清理
page->attribute = 0;
}
}
1.7.下面是几个物理页操作函数
unsigned long page_init(struct Page * page,unsigned long flags)
{
page->attribute |= flags;
if(!page->reference_count || (page->attribute & PG_Shared))
{
page->reference_count++;
page->zone_struct->total_pages_link++;
}
return 1;
}
unsigned long page_clean(struct Page * page)
{
page->reference_count--;
page->zone_struct->total_pages_link--;
if(!page->reference_count)
{
page->attribute &= PG_PTable_Maped;
}
return 1;
}
unsigned long get_page_attribute(struct Page * page)
{
if(page == NULL)
{
color_printk(RED,BLACK,"get_page_attribute() ERROR: page == NULL\n");
return 0;
}
else
return page->attribute;
}
unsigned long set_page_attribute(struct Page * page,unsigned long flags)
{
if(page == NULL)
{
color_printk(RED,BLACK,"set_page_attribute() ERROR: page == NULL\n");
return 0;
}
else
{
page->attribute = flags;
return 1;
}
}