从零开始操作系统-06:Malloc

这一节主要主要是实现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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值