4.5 初级内存管理单元

本文介绍了操作系统中初级内存管理单元的初始化过程,包括获取物理内存信息,初始化Global_Memory_Descriptor结构体,配置内存结构并进行2M页面的分割与管理。在计算可用物理页个数时,采用特定的对齐方式得到1022个有效页面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
	}

输出结果如下图:
Alt
可以看到,采用了“掐头去尾”的方式和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();

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值