4.5 初级内存管理单元
1. 获得物理内存信息
//每条物理地址空间信息占用 20B
struct E820
{
unsigned long address; // 起始地址
unsigned long length; // 长度(单位是字节)
unsigned int type; // 内存类型值: 01H 可用的物理内存
}__attribute__((packed));
可用内存Type=0x0000 0001(9f000h + 7fef0000h)B = 7ff8f000h B ≈ 2047.55 MB 约2 GB
物理页大小为2MB,可用的物理页个数应该为 2047.55 MB / 2 ≈ 1023.775,但是程序运行打印的结果却是1022,为什么?我们看下OS Can Used Total 2M PAGEs的计算方式:
for(i = 0;i <= memory_management_struct.e820_length;i++)
{
unsigned long tmp_start,start,tmp_end,end,mem;
if(memory_management_struct.e820[i].type != 1)
continue;
// 按2M对齐
tmp_start = memory_management_struct.e820[i].address;
start = PAGE_2M_ALIGN(tmp_start);
tmp_end = (memory_management_struct.e820[i].address + memory_management_struct.e820[i].length);
color_printk(RED,BLACK,"memory_management_struct.e820[%d]--tmp_start::%#016x--start::%#016x--tmp_end::%#016x\n",i,tmp_start,start,tmp_end);
end = (tmp_end >> PAGE_2M_SHIFT) << PAGE_2M_SHIFT;
if(end <= start)
continue;
mem = (end - start) >> PAGE_2M_SHIFT;
TotalMem += mem;
color_printk(RED,BLACK,"memory_management_struct.e820[%d]--end::%#016x--mem::%#016x--TotalMem::%#016x\n",i,end,mem,TotalMem);
}
输出结果如下图:
可以看到,采用了“掐头去尾”的方式和2M的内存进行对齐,最终算得可用的物理页个数为 0x3fe = 1022
保存内存信息的结构体
2.初始化 Global_Memory_Descriptor memory_management_struct 结构体实例的成员变量,分配内存并简单初始化
// 此刻的TotalMem是 0x 0000 0000 fffc 0000 + 0x4 0000 = 0x0000 0001 0000 0000 = 0b0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000 0000 0000 0000 0000 = 4294967296 = 4GB
// 先按所有的内存进行分区,不再考虑是否可用
TotalMem = memory_management_struct.e820[memory_management_struct.e820_length].address + memory_management_struct.e820[memory_management_struct.e820_length].length;
//bits map construction init
// end_brk : 0x114a08 bits_map:0x115000
// 从内核程序结束地址 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_size = 0x800 按2MB对齐后物理地址空间可分页数 (800H = 2048D)
// 从 Address:0x00开始的全部:可以用物理段、内存空洞以及ROM空间
// bits_length=0x100 位图长度 (100H = 256D),一个bit对应一个2M的page
memory_management_struct.bits_size = TotalMem >> PAGE_2M_SHIFT; // => 0b0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 000 = 0x800 = 2048
// 8位等于1字节,1位表示一个页
// bits_length是位图长度,以byte为单位计算出长度,然后以0xff进行初始化
memory_management_struct.bits_length = (((unsigned long)(TotalMem >> PAGE_2M_SHIFT) + sizeof(long) * 8 - 1) / 8) & ( ~ (sizeof(long) - 1));
memset(memory_management_struct.bits_map,0xff,memory_management_struct.bits_length); //init bits map memory
//pages construction init
/*
代码清单 4-69 初始化结构体 struct page
*/
// pages construction init
// 在bits_map 之后,找一个4K对齐的地址处开始放 pages_struct
// bits_map_end:0x115800 pages_struct: 0x116000
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);
// bits_size = 0x800 按2MB对齐后物理地址空间可分页数 (800H = 2048D)
// 从 Address:0x00开始的全部:可以用物理段、内存空洞以及ROM空间
// 需要的2M page的数量
memory_management_struct.pages_size = TotalMem >> PAGE_2M_SHIFT;
// 需要的struct Page ,同时按byte计算的数量
memory_management_struct.pages_length = ((TotalMem >> PAGE_2M_SHIFT) * sizeof(struct Page) + sizeof(long) - 1) & ( ~ (sizeof(long) - 1));
// 以0x00 进行初始化 pages_struct 数组
memset(memory_management_struct.pages_struct,0x00,memory_management_struct.pages_length); //init pages memory
//zones construction init
/*
代码清单 4-70 为 struct zone 结构体建立存储空间并对其进行初始化
*/
//zones construction init
// 在 struct page之后 4K边界对齐的地址处放 struct zone
// pages_struct_end:0x12a000 zones_struct: 0x12a000
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);
// 是说有多少个Zone
memory_management_struct.zones_size = 0;
// 是说结构体 struct Zone 数组的长度 暂且算作5个
memory_management_struct.zones_length = (5 * sizeof(struct Zone) + sizeof(long) - 1) & (~(sizeof(long) - 1));
// 以 0x00 初始化 zones_struct 数组
memset(memory_management_struct.zones_struct,0x00,memory_management_struct.zones_length); //init zones memory
3.按实际情况来具体配置 memory_management_struct 结构体各个成员变量的值,同时为有效的Zone进行分割2M page的操作,同时将2M page对应的bit置为0;
// 按实际情况来具体配置 memory_management_struct 结构体各个成员变量的值
for(i = 0;i <= memory_management_struct.e820_length;i++)
{
unsigned long start,end;
struct Zone * z;
struct Page * p;
unsigned long * b;
// 找到一个可用的物理内存段
// 现在只有一个物理内存段可用( TYPE=0x01 且 大小 >2MB )
// Address:0x100000 Length:0x7fef0000 Type=0x0001
// start:0x200000 end:0x7fe00000
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 init
// 一开始这个zones_size是0 同时表示这是第一个可用的zone
//color_printk(WHITE,RED,"List4-71 memory_management_struct.zones_size:%#018lx\n",memory_management_struct.zones_size);
z = memory_management_struct.zones_struct + memory_management_struct.zones_size;
memory_management_struct.zones_size++;
// start:0x200000
z->zone_start_address = start;
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;
z->GMD_struct = &memory_management_struct;
// 本区域包含的struct Page 结构体数量
// 一个可以用的物理页由一个 struct page 结构体管理
// 这里已经是只需要 type=0x01以及>2MB的 可用物理段 了
// 本质上是从物理地址 0x200000 开始的内存
// (也就是说除掉内核程序占用的一部分内存,还有1022个2MB空闲可用物理页)
// 这里z->pages_length的数值就是 0x3fe=1022
z->pages_length = (end - start) >> PAGE_2M_SHIFT;
// bits_map_end:0x115800 pages_struct: 0x116000
// z->pages_group 是struct Page 结构体数组指针,因为 memory_management_struct.pages_struct 代表 memory_management_struct中pages_struct数组的第一项
// (start >> PAGE_2M_SHIFT) 是数组的偏移量
// pages_struct++的话 起始是加 40字节 40D=28H 是一个struct Page 结构占用的字节数
z->pages_group = (struct Page *)(memory_management_struct.pages_struct + (start >> PAGE_2M_SHIFT));
// 由于这里 (start >> PAGE_2M_SHIFT) 计算得到1
// 同时 struct page 结构体大小是 40字节 40D=28H
// 所以在输出截图中 z->pages_group 显示是 0x116028
color_printk(WHITE,RED,"List4-71 start:%#018lx \t start >> PAGE_2M_SHIFT:%#018lx\n",start, start >> PAGE_2M_SHIFT);
color_printk(WHITE,RED,"List4-71 z->pages_length:%#018lx\n",z->pages_length);
color_printk(WHITE,RED,"List4-71 z->pages_group:%#018lx\n",z->pages_group);
//page init
p = z->pages_group;
// 对 Zone z 进行划分 2M page的操作
for(j = 0;j < z->pages_length; j++ , p++)
{
// 每个物理页 属于 哪个 区域
p->zone_struct = z;
// start:0x200000 这是从物理地址0x200000开始分配可用2MB物理页
p->PHY_address = start + PAGE_2M_SIZE * j;
p->attribute = 0;
p->reference_count = 0;
p->age = 0;
// 该物理页对应位图位置 请零 表示该可用物理页空闲
// 由于 bits_map 是一个long类型的数组, >> 6 表示 8byte x 8 = 64 ,一个bits_map元素有64位,代表了64个 2M page
*(memory_management_struct.bits_map + ((p->PHY_address >> PAGE_2M_SHIFT) >> 6)) ^= 1UL << (p->PHY_address >> PAGE_2M_SHIFT) % 64;
// if(j < 10){
// color_printk(RED,BLACK,"memory_management_struct.bits_map::%#018lx----p->PHY_address::%#018lx---(p->PHY_address >> PAGE_2M_SHIFT)::%#018lx-- \
// ----((p->PHY_address >> PAGE_2M_SHIFT) >> 6)::%#018lx----(memory_management_struct.bits_map + ((p->PHY_address >> PAGE_2M_SHIFT) >> 6))::%#018lx----(p->PHY_address >> PAGE_2M_SHIFT) % 64::%#018lx----1UL << (p->PHY_address >> PAGE_2M_SHIFT) % 64::%#018lx\n",memory_management_struct.bits_map,
// p->PHY_address,(p->PHY_address >> PAGE_2M_SHIFT),((p->PHY_address >> PAGE_2M_SHIFT) >> 6),(memory_management_struct.bits_map + ((p->PHY_address >> PAGE_2M_SHIFT) >> 6)),(p->PHY_address >> PAGE_2M_SHIFT) % 64,1UL << (p->PHY_address >> PAGE_2M_SHIFT) % 64);
// }
}
}
4.完善 memory_management_struct 结构体
/////////////init address 0 to page struct 0; because the memory_management_struct.e820[0].type != 1
// 初始化 memory_management_struct.e820 第一个元素
memory_management_struct.pages_struct->zone_struct = memory_management_struct.zones_struct;
memory_management_struct.pages_struct->PHY_address = 0UL;
memory_management_struct.pages_struct->attribute = 0;
memory_management_struct.pages_struct->reference_count = 0;
memory_management_struct.pages_struct->age = 0;
/////////////
// 代码清单 4-70 的时候 假设有5个结果体 现在经过一系列配置 可以计算出真正结构体的数量
// 因为现在只有一个 zone 可用 所以其实 这里的 zones_size是1, 这里的 zones_length就是 0x50 50H=80D
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);
ZONE_DMA_INDEX = 0; //need rewrite in the future
ZONE_NORMAL_INDEX = 0; //need rewrite in the future
for(i = 0;i < memory_management_struct.zones_size /* == 1*/;i++) //need rewrite in the future
{
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);
// 起始地址在0x1 0000 0000 的区域 未曾经过页表映射
if(z->zone_start_address == 0x100000000)
ZONE_UNMAPED_INDEX = i;
}
// end_of_struct 内存页管理结构的结尾地址
// |--- 内核程序 --- | ----------- 内存管理页结构 ---------- |
// |--- 内核程序 --- | ---- bits_map ---- | ---- pages ---- | ----- zones ---- |
// end_of_struct = 0x12a150 预留一段内存空间防止越界访问
// end_of_struct:0x000000000012a150 0x000000000012a050
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
/*
代码清单4-75 初始化 struct Global_Memory_Descriptor 其中使用的结构体 struct Page
结合代码清单4-79 理解 page_init
*/
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;
// 计算出 i == 0
color_printk(RED,BLACK,"memory_management_struct.end_of_struct-----::%#018lx-----Virt_To_Phy(memory_management_struct.end_of_struct)---::%#018lx----i::%#018lx",memory_management_struct.end_of_struct,Virt_To_Phy(memory_management_struct.end_of_struct),i);
// 将 系统内核 与 内存管理单元结构所占物理页的page结构体 全部初始化成
// PG_PTable_Maped(经过页表映射的页) | PG_Kernel_Init(内核初始化程序) | PG_Active(使用中的页) | PG_Kernel(内核层页)属性
for(j = 0;j <= i;j++)
{
page_init(memory_management_struct.pages_struct + j,PG_PTable_Maped | PG_Kernel_Init | PG_Active | PG_Kernel);
}
/*
代码清单 4-76 清空页表项
*/
Global_CR3 = Get_gdt();
// PML4 - PDPT - PDT - PT
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));
// 将PML4页表的前10个页表项清零
// #define Phy_To_Virt(addr) ((unsigned long *)((unsigned long)(addr) + PAGE_OFFSET))
// PML4 单个页表项是8字节 这里强制转换成 unsigned long 类型的指针 +1 就是+8字节
for(i = 0;i < 10;i++)
*(Phy_To_Virt(Global_CR3) + i) = 0UL;
// 强制TLB 自动刷新
flush_tlb();