0x00
完成了以页为单位的内存管理的雏形,更细致的堆内存管理有待后续实现
0x01 位图
为了用尽可能少的空间标记大量内存的使用情况,需要使用名为位图的数据结构。
所谓位图简单来说就是把每一个比特(bit)和对应资源进行映射和标记(map) 。所以叫 bitmap

如何映射呢?很简单,第几位就是第几号资源。
直接放位图的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #ifndef __LIB_KERNEL_BITMAP_H #define __LIB_KERNEL_BITMAP_H #include "global.h" #define BITMAP_MASK 1
struct bitmap{ uint32_t bytes_len; uint8_t* bits; };
void _init_bitmap(struct bitmap* btmp); _Bool _scan_bitmap_test(struct bitmap* btmp, uint32_t bit_offs); int _scan_bitmap(struct bitmap* btmp, uint32_t cnt); void _set_bitmap(struct bitmap* btmp, uint32_t bit_offs, _Bool val);
#endif
|
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include "kernel/bitmap.h" #include "stdint.h" #include "string.h" #include "memfunc.h" #include "kernel/print.h" #include "interrupt.h" #include "debug.h"
void _init_bitmap(struct bitmap* btmp) { memset(btmp->bits, 0, btmp->bytes_len); }
_Bool _scan_bitmap_test(struct bitmap* btmp, uint32_t bit_offs) { uint32_t byte_idx = bit_offs / 8; uint32_t bit_idx = bit_offs % 8; return (btmp->bits[byte_idx] & (BITMAP_MASK << bit_idx)); }
int _scan_bitmap(struct bitmap* btmp, uint32_t cnt) { uint32_t byte_idx = 0; while((0xff == btmp->bits[byte_idx]) && (byte_idx < btmp->bytes_len)) { byte_idx++; } ASSERT(byte_idx < btmp->bytes_len); if(byte_idx == btmp->bytes_len) return -1; int bit_idx = 0; while((uint8_t)(BITMAP_MASK << bit_idx) & btmp->bits[byte_idx]) { bit_idx++; }
int bit_offs = byte_idx * 8 + bit_idx; if(cnt == 1) return bit_offs;
uint32_t bit_remain = (btmp->bytes_len*8 - bit_offs); uint32_t next_bit = bit_offs + 1; uint32_t count = 1;
bit_offs = -1; while(bit_remain-- >0) { if(!(_scan_bitmap_test(btmp, next_bit))) { count++; }else{ count = 0; } if(count == cnt) { bit_offs = next_bit - cnt + 1 ; break; } next_bit++ ; } return bit_offs; }
void _set_bitmap(struct bitmap* btmp, uint32_t bit_offs, _Bool val) { ASSERT((val == 0) || (val == 1)); uint32_t byte_idx = bit_offs / 8; uint32_t bit_odd = bit_offs % 8; if(val) { btmp->bits[byte_idx] |= (BITMAP_MASK << bit_odd); }else{ btmp->bits[byte_idx] &= ~(BITMAP_MASK << bit_odd); } }
|
0x02 内存管理
还记得我们的 loader 建立好了整个内核虚拟地址空间的 PDE,但是只建立了最低 1MB 的 PTE,也就是说仅映射了最低 1MB。
我们当然不能仅仅使用这 1MB 内存。并且已经进入了虚拟地址空间,使用的也就是虚拟内存(我粗浅的理解为虚拟地址+物理内存=虚拟内存)。虚拟地址要映射好物理地址也就是有对应的物理内存才有意义,也就可以称为虚拟内存。
要管理好内存,也就需要搞清楚虚拟地址和物理地址,虚拟内存和物理内存。于是定义几个结构。
1 2 3 4 5 6 7 8 9 10 11 12 13
| struct _pm_pool{ struct bitmap pool_bitmap; uint32_t paddr_start; uint32_t pool_size; };
struct _vm_pool{ struct bitmap pool_bitmap; uint32_t vaddr_start; uint32_t pool_size; };
|
如下图

有关原理在开启分页机制时已经说清楚了,不再赘述。
我们需要做到工作
内存池在内核代码中定义,自然处于内核本身所处的最低 1MB 中。但是位图我们没有直接定义(结构中只是指向位图的指针),我们要做的也简单,就是找一块足够长的内存填上去即可。记得在 loader 中我们只建立了最低 1MB 的映射,也就是说其他虚拟地址我们使用是违法的,所以在最低 1MB 中找一个地址。位图 1bit 对应 1 页,那么 0x1000 bytes长度的位图,就可以管理 128 MB 内存。为了支持 4 GB 内存需要留出 0x20 页来放置位图。最低 1MB 的内存剩余还很宽裕,所以不用担心放不下。
而我们需要管理的内核内存起始地址也是最低 1MB 以后,同时再跳过页目录表和已建立的页表本身。
所谓获取可用虚拟地址,其实就是扫描虚拟内存位图,找到可用的位(为0),置1,返回对应的虚拟地址。所谓申请物理内存页,其实也就是扫描物理内存池中的位图,找到可用位,置1,返回对应的物理地址。所谓的建立映射关系就是在获取的虚拟地址对应的页表项填入申请出的物理页地址。
下面是实现
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
| #include "memory.h" #include "stdint.h" #include "stdtype.h" #include "global.h" #include "debug.h" #include "string.h" #include "memfunc.h" #include "kernel/bitmap.h" #include "kernel/print.h"
#define PG_SIZE 4096 #define PDE_IDX(addr) ((addr & 0xffc00000) >> 22) #define PTE_IDX(addr) ((addr & 0x003ff000) >> 12) #define K_HEAP_START 0xc0100000 #define MEM_BITMAP_BASE 0xc007e000 struct _pm_pool user_pm, kernel_pm; struct _vm_pool kernel_vm;
static uint32_t* pte_ptr(uint32_t vaddr) { uint32_t* pte = (uint32_t*)(0xffc00000 | (PDE_IDX(vaddr) << 12) | (PTE_IDX(vaddr) << 2)); return pte; }
static uint32_t* pde_ptr(uint32_t vaddr) { uint32_t* pde = (uint32_t*)(0xfffff000 | (PDE_IDX(vaddr) << 2)); return pde; }
static void* _get_vaddr(_mem_pool_flag pf, uint32_t pg_cnt) { int vaddr_start = 0, bit_idx_start = -1; uint32_t cnt = 0; if(pf == PF_KERNEL) { bit_idx_start = _scan_bitmap(&kernel_vm.pool_bitmap, pg_cnt); if(bit_idx_start == -1) return NULL; while(cnt < pg_cnt) { _set_bitmap(&kernel_vm.pool_bitmap, bit_idx_start + cnt, 1); cnt++; } vaddr_start = kernel_vm.vaddr_start + bit_idx_start * PG_SIZE; }else{ } return (void*)vaddr_start; }
static void* _palloc_1_p(struct _pm_pool* m_pool) { int bit_idx = _scan_bitmap(&m_pool->pool_bitmap, 1); if(bit_idx == -1) return NULL; _set_bitmap(&m_pool->pool_bitmap, bit_idx, 1); uint32_t page_paddr = ((bit_idx * PG_SIZE) + m_pool->paddr_start); return (void*)page_paddr; }
static void _new_page(void* _vaddr, void* _page_paddr) { uint32_t vaddr = (uint32_t)_vaddr, page_paddr = (uint32_t)_page_paddr; uint32_t* pde = pde_ptr(vaddr); uint32_t* pte = pte_ptr(vaddr); if(*pde & 0x00000001) { ASSERT(!(*pte & 0x00000001)); if(!(*pte & 0x00000001)) { *pte = (page_paddr | PG_US_U | PG_RW_W | PG_P_1); }else{ PANIC("pte repeat"); *pte = (page_paddr | PG_US_U | PG_RW_W | PG_P_1); } }else{ uint32_t pde_paddr = (uint32_t)_palloc_1_p(&kernel_pm); *pde = (pde_paddr | PG_US_U | PG_RW_W | PG_P_1); memset((void*)((uint32_t)pte & 0xfffff000), 0, PG_SIZE); ASSERT(!(*pte & 0x00000001)); *pte = (page_paddr | PG_US_U | PG_RW_W | PG_P_1); } }
void* _valloc_p(_mem_pool_flag pf, uint32_t pg_cnt) { ASSERT(pg_cnt > 0 && pg_cnt < 32512); void* vaddr_start = _get_vaddr(pf, pg_cnt); if(vaddr_start == NULL) return NULL; uint32_t vaddr = (uint32_t)vaddr_start, cnt = pg_cnt; struct _pm_pool* mem_pool = (pf == PF_KERNEL) ? &kernel_pm : &user_pm; while(cnt > 0) { void* page_paddr = _palloc_1_p(mem_pool); if(page_paddr == NULL) return NULL; _new_page((void*)vaddr, page_paddr); vaddr+=PG_SIZE; cnt--; } return vaddr_start; }
void* alloc_kernel_pages(uint32_t pg_cnt) { void* vaddr = _valloc_p(PF_KERNEL, pg_cnt); if(vaddr != NULL) memset(vaddr, 0, pg_cnt * PG_SIZE); return vaddr; }
static void _init_mem_pool(uint32_t all_mem) { print("mem pool init start\n"); uint32_t page_table_used_mem = PG_SIZE * 256; uint32_t used_mem = page_table_used_mem + 0x100000; uint32_t free_mem = all_mem - used_mem; uint32_t all_free_pages = free_mem / PG_SIZE;
uint32_t kernel_free_pages = all_free_pages / 2; uint32_t user_free_pages = all_free_pages - kernel_free_pages; uint32_t kbm_length = (kernel_free_pages+7) / 8; uint32_t ubm_length = (user_free_pages+7) / 8;
uint32_t kp_start = used_mem; uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE; kernel_pm.paddr_start = kp_start; user_pm.paddr_start = up_start;
kernel_pm.pool_size = kernel_free_pages * PG_SIZE; user_pm.pool_size = user_free_pages * PG_SIZE;
kernel_pm.pool_bitmap.bytes_len = kbm_length; user_pm.pool_bitmap.bytes_len = ubm_length;
kernel_pm.pool_bitmap.bits = (void*)(uint32_t)MEM_BITMAP_BASE; user_pm.pool_bitmap.bits = (void*)((uint32_t)MEM_BITMAP_BASE + kbm_length);
print("kernel_pool_bitmap_start:"); print((uint32_t)kernel_pm.pool_bitmap.bits); print(" kernel_pool_phy_addr_start:"); print((uint32_t)kernel_pm.paddr_start); print("\n"); print("user_pool_bitmap_start:"); print((uint32_t)user_pm.pool_bitmap.bits); print(" user_pool_phy_addr_start:"); print((uint32_t)user_pm.paddr_start); print("\n");
_init_bitmap(&kernel_pm.pool_bitmap); _init_bitmap(&user_pm.pool_bitmap);
kernel_vm.pool_bitmap.bytes_len = kbm_length; kernel_vm.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length); kernel_vm.vaddr_start = K_HEAP_START; _init_bitmap(&kernel_vm.pool_bitmap); print(" mem_pool_init done \n"); }
void _init_mem(void) { print("mem init start\n"); uint32_t mem_bytes_total = (*(uint32_t*)(0xc0000c00)); print("all memory:"); print(mem_bytes_total); print("\n"); _init_mem_pool(mem_bytes_total); print("mem init done!\n"); }
|
0x03 验证
main.c
1 2 3 4 5 6 7 8 9 10
| int main(void) { print("kernel made by r3t2\n"); init(); void* addr = alloc_kernel_pages(3); print("get kernel pages start vaddr is "); print((uint32_t)addr); while(1); return 0; }
|

可以看到成功执行。查看一下位图
1 2 3
| (qemu) x/10bx 0xc007e000 c007e000: 0x07 0x00 0x00 0x00 0x00 0x00 0x00 0x00 c007e008: 0x00 0x00
|
查看一下页面是否成功申请并映射
1 2 3 4 5 6 7 8
| (qemu) info tlb ... 00000000c0000000: 0000000000000000 ---DA--UW ... 00000000c00ff000: 00000000000ff000 -------UW 00000000c0100000: 0000000000200000 ---DA--UW 00000000c0101000: 0000000000201000 ---DA--UW 00000000c0102000: 0000000000202000 ---DA--UW
|
可以看到 00000000c0100000 到 00000000c0102000 这三页正是我们申请的。