物理内存管理
由于之前采用了平坦模式,也就是整个内存一段,所以这里就不用管段的处理了。
分页
参考:https://www.cnblogs.com/peterYong/p/6556619.html#_label8
分页就是将主存,程序执行的线性地址空间,还有外存都在逻辑上划分位固定大小的块,这样的好处是降低内存碎片的影响,每一页的大小不能太大,也不能太小,x86采用的是一页4KB,4GB的主存就可以分为2^20个页
对存储空间进行逻辑划分之后,寻址时就要由相应的映射,对于每一个进程来说都有这么一个映射来将自己的逻辑地址映射到物理的地址上,这个映射的结构就是页表,页表也存放在内存中,对于一个进程来说,如果要映射全部4GB的空间需要32/8*2^20=4MB的页表,如果进程多了这会消耗大量内存,显然不合适
x86架构的cpu采用的分页机制是两级页表的方法,即有一个顶级页表,它指向的是一个页表,然后页表指向物理页,顶级页表也就是页表的页表,我们有2^20页,即需要2^20个页表项,一个页表项是4B,一页的大小为4KB,所以需要2^10个页来存放这些页表项,我们的顶级页表映射的就是这2^10个页,所以顶级页表需要的空间也刚好是一页,这样对于一个进程,我们只需要将顶级页表加载入内存就可以了,用到的页表和物理页可以在进程执行的过程中加入,节省了很多空间
这样一个32位的逻辑地址,它的高10位代表的是顶级页表中的偏移量可以映射到一个二级页表,中10位代表的是二级页表中的偏移量映射到一个物理页,最后12位代表的是物理页中的偏移量,这样就找到了物理地址
上面都是理论,现在看实际的代码实现
获取可用的物理内存的大小和地址
GRUB的Mutilboot协议已经帮我们做完了这件事,在之前定义的结构体中就有这些信息
include/mutilboot.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ......
uint32_t mmap_length; uint32_t mmap_addr; ......
typedef struct mmap_entry_t { uint32_t size; uint32_t base_addr_low; uint32_t base_addr_high; uint32_t length_low; uint32_t length_high; uint32_t type; } __attribute__((packed)) mmap_entry_t;
|
GRUB将内存探测结果按分段存储为一个mmap_entry_t数组,数组的首地址是mmap_addr,长度为mmap_length。
我们还要知道内核加载的地址,这一段地址我们不能分配出去
可以根据链接器脚本里的值来确定
scripts/kernel.ld
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ...... . = 0x100000; PROVIDE( kern_start = . ); .text : { *(.text) . = ALIGN(4096); }
.......
.stabstr : { *(.stabstr) . = ALIGN(4096); } PROVIDE( kern_end = . ); ... ...
|
在脚本的开始位置和结束位置定义两个变量kern_start和kern_end在c代码中声明就可以使用了,分别代表内核的起始地址和结束地址
(.代表的是脚本中的当前地址)
打印出来看看
include/pmm.h
1 2 3 4 5 6 7 8 9 10 11 12 13
| #ifndef INCLUDE_PMM_H #define INCLUDE_PMM_H
#include "multiboot.h"
extern uint8_t kern_start[]; extern uint8_t kern_end[];
void show_memory_map(); #endif
|
pmm/pmm.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include "multiboot.h" #include "common.h" #include "pmm.h" #include "debug.h" void show_memory_map() { uint32_t mmap_addr=glb_mboot_ptr->mmap_addr; uint32_t mmap_length=glb_mboot_ptr->mmap_length; mmap_entry_t * mmap= (mmap_entry_t*)mmap_addr; for (uint32_t i=0;i<mmap_length;++i) { printk("base_addr=0x%X%08X,length=0x%X%08X,type=0x%X\n",(uint32_t)mmap[i].base_addr_high,(uint32_t)mmap[i].base_addr_low,(uint32_t)mmap[i].length_high,(uint32_t) mmap[i].length_low,(uint32_t)mmap[i].type); } }
|
修改init/entry.c测试一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include "console.h" #include "timer.h" #include "debug.h" #include "gdt.h" #include "idt.h" #include "pmm.h" int kern_entry() { init_debug(); init_gdt(); init_idt(); console_clear();
printk_color(rc_black, rc_green, "Hello, OS kernel!\n"); init_timer(100); printk("kernel in memory start: 0x%08X\n", kern_start); printk("kernel in memory end: 0x%08X\n", kern_end); printk("kernel in memory used: %d KB\n\n", (kern_end - kern_start +1023) / 1024); show_memory_map(); return 0; }
|
申请和释放内存的实现
内核占了76kb,可用内存段有两段为0x00000000-0x0009FC00和0x00100000-0x07EE0000
第一段是1MB一下的内存段存在很多指向外部设备的地址,我们不去用它,我们用1MB以上的部分,当然要在操作系统加载的结束地址之后,让上限就取到512MB,将这部分的地址按照页的大小放入一个栈中,申请物理地址就从栈中弹出来给申请者,释放就压入栈中
修改include/pmm.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #ifndef INCLUDE_PMM_H #define INCLUDE_PMM_H
#include "multiboot.h"
extern uint8_t kern_start[]; extern uint8_t kern_end[]; extern uint32_t phy_mem_count;
#define PMM_MAX_SIZE 0x20000000 #define PMM_PAGE_SIZE 0x1000 #define PAGE_MAX_SIZE (PMM_MAX_SIZE/PMM_PAGE_SIZE)
void show_memory_map();
void init_pmm(); uint32_t pmm_alloc_page(); void pmm_free_page(uint32_t p); #endif
|
修改pmm/pmm.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include "multiboot.h" #include "common.h" #include "pmm.h" #include "debug.h" static uint32_t pmm_stack[PAGE_MAX_SIZE+1]; static uint32_t pmm_stack_top; uint32_t phy_mem_count; void show_memory_map() { uint32_t mmap_addr=glb_mboot_ptr->mmap_addr; uint32_t mmap_length=glb_mboot_ptr->mmap_length; mmap_entry_t * mmap= (mmap_entry_t*)mmap_addr; for (mmap = (mmap_entry_t *)mmap_addr; (uint32_t)mmap < mmap_addr + mmap_length; mmap++) { printk("base_addr=0x%X%08X,length=0x%X%08X,type=0x%X\n",(uint32_t)mmap->base_addr_high,(uint32_t)mmap->base_addr_low,(uint32_t)mmap->length_high,(uint32_t) mmap->length_low,(uint32_t)mmap->type); } } void init_pmm() { mmap_entry_t * mmap_start_addr=(mmap_entry_t*)glb_mboot_ptr->mmap_addr; mmap_entry_t * mmap_end_addr=(mmap_entry_t*)glb_mboot_ptr->mmap_addr+glb_mboot_ptr->mmap_length; mmap_entry_t *mmap_entry; for(mmap_entry=mmap_start_addr;mmap_entry<mmap_end_addr;mmap_entry++) { if(mmap_entry->type==1&&mmap_entry->base_addr_low==0x00100000) { uint32_t page_addr=mmap_entry->base_addr_low+(kern_end-kern_start); uint32_t length=mmap_entry->base_addr_low+mmap_entry->length_low; while(page_addr<=PMM_MAX_SIZE&&page_addr<length) { pmm_free_page(page_addr); page_addr+=PMM_PAGE_SIZE; phy_mem_count++; } } } }
void pmm_free_page(uint32_t p) { assert(pmm_stack_top != PAGE_MAX_SIZE , "out of pmm_stack stack"); pmm_stack[++pmm_stack_top]=p; }
uint32_t pmm_alloc_page () { assert(pmm_stack_top!=0,"out of memory"); uint32_t page=pmm_stack[pmm_stack_top--]; return page; }
|
修改entry.c测试一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include "console.h" #include "timer.h" #include "debug.h" #include "gdt.h" #include "idt.h" #include "pmm.h" int kern_entry() { init_debug(); init_gdt(); init_idt(); console_clear();
printk_color(rc_black, rc_green, "Hello, OS kernel!\n"); init_timer(100); printk("kernel in memory start: 0x%08X\n", kern_start); printk("kernel in memory end: 0x%08X\n", kern_end); printk("kernel in memory used: %d KB\n\n", (kern_end - kern_start +1023) / 1024); show_memory_map(); init_pmm();
printk_color(rc_black, rc_red, "\nThe Count of Physical Memory Page is: %u\n\n", phy_mem_count);
uint32_t allc_addr = NULL; printk_color(rc_black, rc_light_brown , "Test Physical Memory Alloc :\n"); allc_addr = pmm_alloc_page(); printk_color(rc_black, rc_light_brown , "Alloc Physical Addr: 0x%08X\n",allc_addr); allc_addr = pmm_alloc_page(); printk_color(rc_black, rc_light_brown , "Alloc Physical Addr: 0x%08X\n",allc_addr); allc_addr = pmm_alloc_page(); printk_color(rc_black, rc_light_brown , "Alloc Physical Addr: 0x%08X\n",allc_addr); allc_addr = pmm_alloc_page(); printk_color(rc_black, rc_light_brown , "Alloc Physical Addr: 0x%08X\n",allc_addr); return 0; }
|