diff --git a/kernel/mm/mm.c b/kernel/mm/mm.c index 4efc0863..20b0d449 100644 --- a/kernel/mm/mm.c +++ b/kernel/mm/mm.c @@ -8,6 +8,18 @@ ul Total_Memory = 0; ul total_2M_pages = 0; static ul root_page_table_phys_addr = 0; // 内核层根页表的物理地址 + +/** + * @brief 从页表中获取pdt页表项的内容 + * + * @param proc_page_table_addr 页表的地址 + * @param is_phys 页表地址是否为物理地址 + * @param virt_addr_start 要清除的虚拟地址的起始地址 + * @param length 要清除的区域的长度 + * @param clear 是否清除标志位 + */ +uint64_t mm_get_PDE(ul proc_page_table_addr, bool is_phys, ul virt_addr, bool clear); + void mm_init() { kinfo("Initializing memory management unit..."); @@ -444,36 +456,6 @@ void page_table_init() for (int j = 0; j < z->count_pages; ++j) { mm_map_phys_addr((ul)phys_2_virt(p->addr_phys), p->addr_phys, PAGE_2M_SIZE, PAGE_KERNEL_PAGE); - /* - // 计算出PML4页表中的页表项的地址 - tmp_addr = (ul *)((ul)pml4_addr + ((((ul)phys_2_virt(p->addr_phys)) >> PAGE_GDT_SHIFT) & 0x1ff) * 8); - - // 说明该页还没有分配pdpt页表,使用kmalloc分配一个 - if (*tmp_addr = 0) - { - ul *virt_addr = kmalloc(PAGE_4K_SIZE, 0); - set_pml4t(tmp_addr, mk_pml4t(virt_2_phys(virt_addr), PAGE_KERNEL_PGT)); - } - - // 计算出pdpt页表的页表项的地址 - tmp_addr = (ul *)((ul)(phys_2_virt(*tmp_addr & (~0xfffUL))) + ((((ul)phys_2_virt(p->addr_phys)) >> PAGE_1G_SHIFT) & 0x1ff) * 8); - - // 说明该页还没有分配pd页表,使用kmalloc分配一个 - if (*tmp_addr = 0) - { - ul *virt_addr = kmalloc(PAGE_4K_SIZE, 0); - set_pdpt(tmp_addr, mk_pdpt(virt_2_phys(virt_addr), PAGE_KERNEL_DIR)); - } - - // 计算出pd页表的页表项的地址 - tmp_addr = (ul *)((ul)(phys_2_virt(*tmp_addr & (~0xfffUL))) + ((((ul)phys_2_virt(p->addr_phys)) >> PAGE_2M_SHIFT) & 0x1ff) * 8); - - // 填入pd页表的页表项,映射2MB物理页 - set_pdt(tmp_addr, mk_pdt(virt_2_phys(p->addr_phys), PAGE_KERNEL_PAGE)); - */ - // 测试 - if (j % 50 == 0) - kdebug("pd_addr=%#018lx, *pd_addr=%#018lx", tmp_addr, *tmp_addr); } } @@ -631,19 +613,14 @@ void mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_ else tmp = (ul *)((ul)proc_page_table_addr & (~0xfffUL)) + ((virt_addr_start >> PAGE_GDT_SHIFT) & 0x1ff); - // kdebug("tmp = %#018lx", tmp); if (*tmp == 0) { ul *virt_addr = kmalloc(PAGE_4K_SIZE, 0); memset(virt_addr, 0, PAGE_4K_SIZE); set_pml4t(tmp, mk_pml4t(virt_2_phys(virt_addr), (user ? PAGE_USER_PGT : PAGE_KERNEL_PGT))); } - // kdebug("*tmp = %#018lx", *tmp); - if (is_phys) - tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((virt_addr_start >> PAGE_1G_SHIFT) & 0x1ff)); - else - tmp = (ul *)(*tmp & (~0xfffUL)) + ((virt_addr_start >> PAGE_1G_SHIFT) & 0x1ff); + tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((virt_addr_start >> PAGE_1G_SHIFT) & 0x1ff)); if (*tmp == 0) { @@ -657,10 +634,8 @@ void mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_ for (ul i = 0; i < (length); i += PAGE_2M_SIZE) { // 计算当前2M物理页对应的pdt的页表项的物理地址 - if (is_phys) - tmp1 = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr_start + i) >> PAGE_2M_SHIFT) & 0x1ff))); - else - tmp1 = ((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr_start + i) >> PAGE_2M_SHIFT) & 0x1ff)); + + tmp1 = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr_start + i) >> PAGE_2M_SHIFT) & 0x1ff))); // 页面写穿,禁止缓存 set_pdt(tmp1, mk_pdt((ul)phys_addr_start + i, flags | (user ? PAGE_USER_PAGE : PAGE_KERNEL_PAGE))); @@ -669,6 +644,148 @@ void mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_ flush_tlb(); } +/** + * @brief 从页表中获取pdt页表项的内容 + * + * @param proc_page_table_addr 页表的地址 + * @param is_phys 页表地址是否为物理地址 + * @param virt_addr_start 要清除的虚拟地址的起始地址 + * @param length 要清除的区域的长度 + * @param clear 是否清除标志位 + */ +uint64_t mm_get_PDE(ul proc_page_table_addr, bool is_phys, ul virt_addr, bool clear) +{ + ul *tmp; + if (is_phys) + tmp = phys_2_virt((ul *)((ul)proc_page_table_addr & (~0xfffUL)) + ((virt_addr >> PAGE_GDT_SHIFT) & 0x1ff)); + else + tmp = (ul *)((ul)proc_page_table_addr & (~0xfffUL)) + ((virt_addr >> PAGE_GDT_SHIFT) & 0x1ff); + + // pml4页表项为0 + if (*tmp == 0) + return 0; + + tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((virt_addr >> PAGE_1G_SHIFT) & 0x1ff)); + + // pdpt页表项为0 + if (*tmp == 0) + return 0; + + // 读取pdt页表项 + tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr) >> PAGE_2M_SHIFT) & 0x1ff))); + + if (clear) // 清除页表项的标志位 + return *tmp & (~0x1fff); + else + return *tmp; +} + +/** + * @brief 从页表中清除虚拟地址的映射 + * + * @param proc_page_table_addr 页表的地址 + * @param is_phys 页表地址是否为物理地址 + * @param virt_addr_start 要清除的虚拟地址的起始地址 + * @param length 要清除的区域的长度 + */ +void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul length) +{ + ul *tmp; + if (is_phys) + tmp = phys_2_virt((ul *)((ul)proc_page_table_addr & (~0xfffUL)) + ((virt_addr_start >> PAGE_GDT_SHIFT) & 0x1ff)); + else + tmp = (ul *)((ul)proc_page_table_addr & (~0xfffUL)) + ((virt_addr_start >> PAGE_GDT_SHIFT) & 0x1ff); + + // pml4页表项为0 + if (*tmp == 0) + return; + + tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((virt_addr_start >> PAGE_1G_SHIFT) & 0x1ff)); + + // pdpt页表项为0 + if (*tmp == 0) + return; + + ul *tmp1; + + for (ul i = 0; i < (length); i += PAGE_2M_SIZE) + { + // 计算当前2M物理页对应的pdt的页表项的物理地址 + tmp1 = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr_start + i) >> PAGE_2M_SHIFT) & 0x1ff))); + // 清除映射 + *tmp1 = 0; + } + + flush_tlb(); +} + +/** + * @brief 从mms中寻找Page结构体 + * + * @param phys_addr + * @return struct Page* + */ +static struct Page *mm_find_page(uint64_t phys_addr, uint32_t zone_select) +{ + uint32_t zone_start, zone_end; + switch (zone_select) + { + case ZONE_DMA: + // DMA区域 + zone_start = 0; + zone_end = ZONE_DMA_INDEX; + break; + case ZONE_NORMAL: + zone_start = ZONE_DMA_INDEX; + zone_end = ZONE_NORMAL_INDEX; + break; + case ZONE_UNMAPPED_IN_PGT: + zone_start = ZONE_NORMAL_INDEX; + zone_end = ZONE_UNMAPPED_INDEX; + break; + + default: + kerror("In mm_find_page: param: zone_select incorrect."); + // 返回空 + return NULL; + break; + } + + for (int i = zone_start; i <= zone_end; ++i) + { + if ((memory_management_struct.zones_struct + i)->count_pages_using == 0) + continue; + + struct Zone *z = memory_management_struct.zones_struct + i; + + // 区域对应的起止页号 + ul page_start = (z->zone_addr_start >> PAGE_2M_SHIFT); + ul page_end = (z->zone_addr_end >> PAGE_2M_SHIFT); + + ul tmp = 64 - page_start % 64; + for (ul j = page_start; j < page_end; j += ((j % 64) ? tmp : 64)) + { + // 按照bmp中的每一个元素进行查找 + // 先将p定位到bmp的起始元素 + ul *p = memory_management_struct.bmp + (j >> 6); + + ul shift = j % 64; + for (ul k = shift; k < 64; ++k) + { + if ((*p >> k) & 1) // 若当前页已分配 + { + uint64_t page_num = j + k - shift; + struct Page *x = memory_management_struct.pages_struct + page_num; + + if (x->addr_phys == phys_addr) // 找到对应的页 + return x; + } + } + } + } + return NULL; +} + /** * @brief 调整堆区域的大小(暂时只能增加堆区域) * @@ -679,16 +796,38 @@ void mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_ */ uint64_t mm_do_brk(uint64_t old_brk_end_addr, int64_t offset) { - // 暂不支持缩小堆内存 - if (offset < 0) - return old_brk_end_addr; - uint64_t end_addr = old_brk_end_addr + offset; - for (uint64_t i = old_brk_end_addr; i < end_addr; i += PAGE_2M_SIZE) + uint64_t end_addr = PAGE_2M_ALIGN(old_brk_end_addr + offset); + if (offset >= 0) { - kdebug("map [%d]", i); - mm_map_proc_page_table((uint64_t)current_pcb->mm->pgd, true, i, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, PAGE_2M_SIZE, PAGE_USER_PAGE, true); + for (uint64_t i = old_brk_end_addr; i < end_addr; i += PAGE_2M_SIZE) + { + kdebug("map [%#018lx]", i); + mm_map_proc_page_table((uint64_t)current_pcb->mm->pgd, true, i, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, PAGE_2M_SIZE, PAGE_USER_PAGE, true); + } + current_pcb->mm->brk_end = end_addr; + } + else + { + + // 释放堆内存 + for (uint64_t i = end_addr; i < old_brk_end_addr; i += PAGE_2M_SIZE) + { + uint64_t phys = mm_get_PDE((uint64_t)phys_2_virt((uint64_t)current_pcb->mm->pgd), false, i, true); + + // 找到对应的页 + struct Page *p = mm_find_page(phys, ZONE_NORMAL); + if (p == NULL) + { + kerror("cannot find page addr=%#018lx", phys); + return end_addr; + } + + free_pages(p, 1); + } + + mm_unmap_proc_table((uint64_t)phys_2_virt((uint64_t)current_pcb->mm->pgd), false, end_addr, PAGE_2M_ALIGN(ABS(offset))); + // 在页表中取消映射 } - current_pcb->mm->brk_end = end_addr; return end_addr; } \ No newline at end of file diff --git a/kernel/sched/sched.c b/kernel/sched/sched.c index 3d15f1da..f6ff843f 100644 --- a/kernel/sched/sched.c +++ b/kernel/sched/sched.c @@ -53,7 +53,7 @@ void sched_cfs() cli(); current_pcb->flags &= ~PF_NEED_SCHED; struct process_control_block *proc = sched_cfs_dequeue(); - + // kdebug("sched_cfs_ready_queue[proc_current_cpu_id].count = %d", sched_cfs_ready_queue[proc_current_cpu_id].count); if (current_pcb->virtual_runtime >= proc->virtual_runtime || current_pcb->state != PROC_RUNNING) // 当前进程运行时间大于了下一进程的运行时间,进行切换 { diff --git a/kernel/syscall/syscall.c b/kernel/syscall/syscall.c index af79fda9..d03e66ca 100644 --- a/kernel/syscall/syscall.c +++ b/kernel/syscall/syscall.c @@ -357,7 +357,7 @@ uint64_t sys_brk(struct pt_regs *regs) { uint64_t new_brk = PAGE_2M_ALIGN(regs->r8); - kdebug("sys_brk input= %#010lx , new_brk= %#010lx bytes current_pcb->mm->brk_start=%#018lx current->end_brk=%#018lx", regs->r8, new_brk, current_pcb->mm->brk_start, current_pcb->mm->brk_end); + // kdebug("sys_brk input= %#010lx , new_brk= %#010lx bytes current_pcb->mm->brk_start=%#018lx current->end_brk=%#018lx", regs->r8, new_brk, current_pcb->mm->brk_start, current_pcb->mm->brk_end); if ((int64_t)regs->r8 == -1) { @@ -372,10 +372,20 @@ uint64_t sys_brk(struct pt_regs *regs) if (new_brk > current_pcb->addr_limit) // 堆地址空间超过限制 return -ENOMEM; - if (new_brk < current_pcb->mm->brk_end) // todo: 释放堆内存空间 - return 0; + int64_t offset; + if (new_brk >= current_pcb->mm->brk_end) + offset = (int64_t)(new_brk - current_pcb->mm->brk_end); + else + offset = -(int64_t)(current_pcb->mm->brk_end - new_brk); - new_brk = mm_do_brk(current_pcb->mm->brk_end, new_brk - current_pcb->mm->brk_end); // 扩展堆内存空间 + /* + if (offset < 0) + { + kdebug("decrease brk, offset = %#010lx", (uint64_t)(-offset)); + } + */ + + new_brk = mm_do_brk(current_pcb->mm->brk_end, offset); // 扩展堆内存空间 current_pcb->mm->brk_end = new_brk; return 0; @@ -390,12 +400,25 @@ uint64_t sys_brk(struct pt_regs *regs) uint64_t sys_sbrk(struct pt_regs *regs) { uint64_t retval = current_pcb->mm->brk_end; - regs->r8 = (int64_t)current_pcb->mm->brk_end + (int64_t)regs->r8; + if ((int64_t)regs->r8 > 0) + { - if (sys_brk(regs) == 0) - return retval; + uint64_t new_brk = PAGE_2M_ALIGN(retval + regs->r8); + if (new_brk > current_pcb->addr_limit) // 堆地址空间超过限制 + { + kdebug("exceed mem limit, new_brk = %#018lx", new_brk); + return -ENOMEM; + } + } else - return -ENOMEM; + { + if ((__int128_t)current_pcb->mm->brk_end + (__int128_t)regs->r8 < current_pcb->mm->brk_start) + return retval; + } + uint64_t new_brk = mm_do_brk(current_pcb->mm->brk_end, (int64_t)regs->r8); // 调整堆内存空间 + + current_pcb->mm->brk_end = new_brk; + return retval; } ul sys_ahci_end_req(struct pt_regs *regs) @@ -407,7 +430,7 @@ ul sys_ahci_end_req(struct pt_regs *regs) // 系统调用的内核入口程序 void do_syscall_int(struct pt_regs *regs, unsigned long error_code) { - + ul ret = system_call_table[regs->rax](regs); regs->rax = ret; // 返回码 } diff --git a/user/init.c b/user/init.c index d0a11bc3..33f41711 100644 --- a/user/init.c +++ b/user/init.c @@ -45,9 +45,10 @@ int main() if (*(uint64_t *)((uint64_t)(ptr[i]) - sizeof(uint64_t)) > 0x4008) printf("[%d] start_addr = %#018lx, len = %#010lx\n", (uint64_t)(ptr[i]) - 8, *(uint64_t *)((uint64_t)(ptr[i]) - sizeof(uint64_t))); } - printf("ptr[0]->len=%lld\n", *(uint64_t *)((uint64_t)ptr[0] - sizeof(uint64_t))); - printf("ptr[1]->len=%lld\n", *(uint64_t *)((uint64_t)ptr[1] - sizeof(uint64_t))); - // printf("ptr[24]->len=%lld\n", *(uint64_t*)((uint64_t)ptr[24] - sizeof(uint64_t))); + + // printf("ptr[0]->len=%lld\n", *(uint64_t *)((uint64_t)ptr[0] - sizeof(uint64_t))); + // printf("ptr[1]->len=%lld\n", *(uint64_t *)((uint64_t)ptr[1] - sizeof(uint64_t))); + // printf("ptr[24]->len=%lld\n", *(uint64_t*)((uint64_t)ptr[24] - sizeof(uint64_t))); printf("alloc done. total used: %lld bytes\n", js); printf("try to free...\n"); for (int i = 0; i < 256; ++i) diff --git a/user/libs/libc/malloc.c b/user/libs/libc/malloc.c index 2eba34c7..03f0c3e3 100644 --- a/user/libs/libc/malloc.c +++ b/user/libs/libc/malloc.c @@ -5,6 +5,24 @@ #include #include +#define PAGE_4K_SHIFT 12 +#define PAGE_2M_SHIFT 21 +#define PAGE_1G_SHIFT 30 +#define PAGE_GDT_SHIFT 39 + +// 不同大小的页的容量 +#define PAGE_4K_SIZE (1UL << PAGE_4K_SHIFT) +#define PAGE_2M_SIZE (1UL << PAGE_2M_SHIFT) +#define PAGE_1G_SIZE (1UL << PAGE_1G_SHIFT) + +// 屏蔽低于x的数值 +#define PAGE_4K_MASK (~(PAGE_4K_SIZE - 1)) +#define PAGE_2M_MASK (~(PAGE_2M_SIZE - 1)) + +// 将addr按照x的上边界对齐 +#define PAGE_4K_ALIGN(addr) (((unsigned long)(addr) + PAGE_4K_SIZE - 1) & PAGE_4K_MASK) +#define PAGE_2M_ALIGN(addr) (((unsigned long)(addr) + PAGE_2M_SIZE - 1) & PAGE_2M_MASK) + /** * @brief 显式链表的结点 * @@ -23,14 +41,9 @@ static uint64_t brk_managed_addr = 0; // 堆区域已经被管理的地址 // 空闲链表 // 按start_addr升序排序 static malloc_mem_chunk_t *malloc_free_list = NULL; +static malloc_mem_chunk_t *malloc_free_list_end = NULL; // 空闲链表的末尾结点 -/** - * @brief 获取一块堆内存(不尝试扩大堆内存) - * - * @param size - * @return void* 内存的地址指针,获取失败时返回-ENOMEM - */ -static void *malloc_no_enlarge(ssize_t size); +static uint64_t count_last_free_size = 0; // 统计距离上一次回收内存,已经free了多少内存 /** * @brief 将块插入空闲链表 @@ -39,6 +52,12 @@ static void *malloc_no_enlarge(ssize_t size); */ static void malloc_insert_free_list(malloc_mem_chunk_t *ck); +/** + * @brief 当堆顶空闲空间大于2个页的空间的时候,释放1个页 + * + */ +static void release_brk(); + /** * @brief 在链表中检索符合要求的空闲块(best fit) * @@ -52,7 +71,6 @@ static malloc_mem_chunk_t *malloc_query_free_chunk_bf(uint64_t size) if (malloc_free_list == NULL) { - printf("free list is none.\n"); return NULL; } malloc_mem_chunk_t *ptr = malloc_free_list; @@ -114,15 +132,16 @@ static int malloc_enlarge(int32_t size) if (brk_base_addr == 0) // 第一次调用,需要初始化 { brk_base_addr = brk(-1); - printf("brk_base_addr=%#018lx\n", brk_base_addr); + // printf("brk_base_addr=%#018lx\n", brk_base_addr); brk_managed_addr = brk_base_addr; brk_max_addr = brk(-2); } - int64_t tmp = brk_managed_addr + size - brk_max_addr; - if (tmp > 0) // 现有堆空间不足 + int64_t free_space = brk_max_addr - brk_managed_addr; + + if (free_space < size) // 现有堆空间不足 { - if (sbrk(tmp) != (void *)(-1)) + if (sbrk(size - free_space) != (void *)(-1)) brk_max_addr = brk((-2)); else { @@ -133,10 +152,12 @@ static int malloc_enlarge(int32_t size) // 扩展管理的堆空间 // 在新分配的内存的底部放置header + // printf("managed addr = %#018lx\n", brk_managed_addr); malloc_mem_chunk_t *new_ck = (malloc_mem_chunk_t *)brk_managed_addr; new_ck->length = brk_max_addr - brk_managed_addr; - printf("new_ck->start_addr=%#018lx\tbrk_max_addr=%#018lx\tbrk_managed_addr=%#018lx\n", (uint64_t)new_ck, brk_max_addr, brk_managed_addr); - new_ck->prev = new_ck->next = NULL; + // printf("new_ck->start_addr=%#018lx\tbrk_max_addr=%#018lx\tbrk_managed_addr=%#018lx\n", (uint64_t)new_ck, brk_max_addr, brk_managed_addr); + new_ck->prev = NULL; + new_ck->next = NULL; brk_managed_addr = brk_max_addr; malloc_insert_free_list(new_ck); @@ -153,14 +174,19 @@ static void malloc_merge_free_chunk() if (malloc_free_list == NULL) return; malloc_mem_chunk_t *ptr = malloc_free_list->next; - while (ptr) + while (ptr != NULL) { // 内存块连续 if (((uint64_t)(ptr->prev) + ptr->prev->length == (uint64_t)ptr)) { + // printf("merged %#018lx and %#018lx\n", (uint64_t)ptr, (uint64_t)(ptr->prev)); // 将ptr与前面的空闲块合并 ptr->prev->length += ptr->length; ptr->prev->next = ptr->next; + if (ptr->next == NULL) + malloc_free_list_end = ptr->prev; + else + ptr->next->prev = ptr->prev; // 由于内存组成结构的原因,不需要free掉header ptr = ptr->prev; } @@ -178,14 +204,15 @@ static void malloc_insert_free_list(malloc_mem_chunk_t *ck) if (malloc_free_list == NULL) // 空闲链表为空 { malloc_free_list = ck; + malloc_free_list_end = ck; ck->prev = ck->next = NULL; return; } else { - uint64_t ck_end = (uint64_t)ck + ck->length; + malloc_mem_chunk_t *ptr = malloc_free_list; - while (ptr) + while (ptr != NULL) { if ((uint64_t)ptr < (uint64_t)ck) { @@ -194,13 +221,14 @@ static void malloc_insert_free_list(malloc_mem_chunk_t *ck) ptr->next = ck; ck->next = NULL; ck->prev = ptr; + malloc_free_list_end = ck; break; } else if ((uint64_t)(ptr->next) > (uint64_t)ck) { ck->prev = ptr; ck->next = ptr->next; - ck->prev->next = ck; + ptr->next = ck; ck->next->prev = ck; break; } @@ -254,7 +282,6 @@ void *malloc(ssize_t size) { // 尝试合并空闲块 - printf("merge\n"); malloc_merge_free_chunk(); ck = malloc_query_free_chunk_bf(size); @@ -262,11 +289,11 @@ void *malloc(ssize_t size) if (ck) goto found; // 找不到合适的块,扩容堆区域 - printf("enlarge\n"); + if (malloc_enlarge(size) == -ENOMEM) return (void *)-ENOMEM; // 内存不足 // 扩容后再次尝试获取 - printf("query\n"); + ck = malloc_query_free_chunk_bf(size); } found:; @@ -285,9 +312,11 @@ found:; if (ck->next != NULL) // 当前不是最后一个块 ck->next->prev = ck->prev; + else + malloc_free_list_end = ck->prev; // 当前块剩余的空间还能容纳多一个结点的空间,则分裂当前块 - if (ck->length - size > sizeof(malloc_mem_chunk_t)) + if ((int64_t)(ck->length) - size > sizeof(malloc_mem_chunk_t)) { malloc_mem_chunk_t *new_ck = (malloc_mem_chunk_t *)(((uint64_t)ck) + size); new_ck->length = ck->length - size; @@ -297,11 +326,34 @@ found:; malloc_insert_free_list(new_ck); } - // printf("ck=%lld\n", (uint64_t)ck); + // 此时链表结点的指针的空间被分配出去 return (void *)((uint64_t)ck + sizeof(uint64_t)); } +/** + * @brief 当堆顶空闲空间大于2个页的空间的时候,释放1个页 + * + */ +static void release_brk() +{ + // 先检测最顶上的块 + // 由于块按照开始地址排列,因此找最后一个块 + if (malloc_free_list_end == NULL) + return; + if ((uint64_t)malloc_free_list_end + malloc_free_list_end->length == brk_max_addr && (uint64_t)malloc_free_list_end <= brk_max_addr - (PAGE_2M_SIZE << 1)) + { + int64_t delta = (brk_max_addr - (uint64_t)malloc_free_list_end) & PAGE_2M_MASK - PAGE_2M_SIZE; + + if (delta <= 0) // 不用释放内存 + return; + sbrk(-delta); + brk_max_addr = brk(-2); + brk_managed_addr = brk_max_addr; + + malloc_free_list_end->length = brk_max_addr - (uint64_t)malloc_free_list_end; + } +} /** * @brief 释放一块堆内存 * @@ -310,7 +362,15 @@ found:; void free(void *ptr) { // 找到结点(此时prev和next都处于未初始化的状态) - malloc_mem_chunk_t * ck = (malloc_mem_chunk_t *)((uint64_t)ptr-sizeof(uint64_t)); + malloc_mem_chunk_t *ck = (malloc_mem_chunk_t *)((uint64_t)ptr - sizeof(uint64_t)); // printf("free(): addr = %#018lx\t len=%#018lx\n", (uint64_t)ck, ck->length); + count_last_free_size += ck->length; malloc_insert_free_list(ck); + + if (count_last_free_size > PAGE_2M_SIZE) + { + count_last_free_size = 0; + malloc_merge_free_chunk(); + release_brk(); + } } diff --git a/user/libs/libc/unistd.h b/user/libs/libc/unistd.h index 7d15cd22..28494b94 100644 --- a/user/libs/libc/unistd.h +++ b/user/libs/libc/unistd.h @@ -63,10 +63,10 @@ pid_t vfork(void); * @brief 将堆内存调整为end_brk * * @param end_brk 新的堆区域的结束地址 - * end_brk=0 ===> 返回堆区域的起始地址 - * end_brk=-1 ===> 返回堆区域的结束地址 + * end_brk=-1 ===> 返回堆区域的起始地址 + * end_brk=-2 ===> 返回堆区域的结束地址 * @return uint64_t 错误码 - * + * */ uint64_t brk(uint64_t end_brk);