这一节主要主要是实现Malloc。
所需要的文件在Github:https://github.com/yongkangluo/Ubuntu20.04OS/tree/main/Files/Lec6-Malloc
前备知识:隐式空闲链表(Implicit Free List)
1. 动态分配总览
每一块可以设置成两个字节的大小,并保持对齐。
2. 堆块的格式
这个块设计的是8个字节对齐的。
类似的,可以设计4个字节的的堆块格式。其中 A 表示是否分配,PF = 1 表示前一区块空闲,为 0 表示不空闲。
其中合并方式如下:
为了方便合并,可以增加一个头块,和尾部块。
实现
// kalloc.c
int kalloc_init()
{
// 这里的 __kernel_heap_start 表示为堆栈的起始地址
__kalloc_kheap.start = &__kernel_heap_start;
__kalloc_kheap.brk = NULL;
__kalloc_kheap.max_addr = (void*)KSTACK_START;
if (!dmm_init(&__kalloc_kheap)) {
return 0;
}
SW(__kalloc_kheap.start, PACK(4, M_ALLOCATED));
SW(__kalloc_kheap.start + WSIZE, PACK(0, M_ALLOCATED));
__kalloc_kheap.brk += WSIZE;
// 向上拓展一个 page 的字节
return lx_grow_heap(&__kalloc_kheap, HEAP_INIT_SIZE) != NULL;
}
......
// 当页面不够的时候,需要对堆栈扩展
void*
lx_grow_heap(heap_context_t* heap, size_t sz)
{
// 将heap向上拓展 sz 个字节
void* start;
// 加上 WSIZE 防止尾标记没地方写
if (!(start = lxbrk(heap, sz + WSIZE))) {
return NULL;
}
sz = ROUNDUP(sz, BOUNDARY);
// 保证heap.brk 之前尾标记的前面
heap->brk -= WSIZE;
uint32_t old_marker = *((uint32_t*)start);
uint32_t free_hdr = PACK(sz, CHUNK_PF(old_marker));
SW(start, free_hdr);
printf("heap Address: %p \n", FPTR(start, sz));
SW(FPTR(start, sz), free_hdr);
SW(NEXT_CHK(start), PACK(0, M_ALLOCATED | M_PREV_FREE));
}
其中,dmm
// dmm.c
int
dmm_init(heap_context_t* heap){
assert((uintptr_t) heap -> start % BOUNDARY == 0);
// 开始字节是不是对齐的
heap -> brk = heap-> start;
// 为起始地址分配虚拟页面
return vmm_alloc_page(heap->brk, PG_PREM_RW) != NULL;
}
......
void*
lxbrk(heap_context_t * heap, size_t size){
if(size == 0){
return heap->brk;
}
void* current_brk = heap->brk;
void* next = current_brk + ROUNDUP(size, BOUNDARY);
if(next >= heap->max_addr || next < current_brk){
//防止超过上界和重写
return NULL;
}
uintptr_t diff = PG_ALIGN(next) - PG_ALIGN(current_brk);
if(diff){
if(!vmm_alloc_pages((void*)(PG_ALIGN(current_brk) + PG_SIZE), diff, PG_PREM_RW)){
// assert_msg(0, "unable to brk");
return NULL;
}
}
heap->brk += size;
return current_brk;
}
最终结果可以在kmain函数里试验一下:
uint8_t** arr = (uint8_t**) lxmalloc(10 * sizeof(uint8_t*));
for (size_t i = 0; i < 10; i++)
{
arr[i] = (uint8_t*) lxmalloc((i + 1) * 2);
}
for(size_t i = 0; i < 10; ++i){
lxfree(arr[i]);
}
uint8_t* big_ = lxmalloc(8296);
big_[0] = 123;
big_[1] = 13;
big_[2] = 23;
printf("%u, %u, %u", big_[0], big_[1], big_[2]);
lxfree(arr);
lxfree(big_);
最终结果图:
总结:
自己做的时候,难点其实在页面扩展的时候。虚拟映射,因为之前的函数没有写根据页面大小来分配的,所以要自己调整一下映射函数。
方法主要跟着B站Up主做的,B站视频链接在:https://www.bilibili.com/video/BV1jL4y1s7X6/?spm_id_from=333.788&vd_source=72ce864f895f9fbf22b81450817f2875