Browse Source

:new: 回收堆内存给操作系统

fslongjin 2 years ago
parent
commit
1cc3db5e88
6 changed files with 311 additions and 88 deletions
  1. 187 48
      kernel/mm/mm.c
  2. 1 1
      kernel/sched/sched.c
  3. 32 9
      kernel/syscall/syscall.c
  4. 4 3
      user/init.c
  5. 84 24
      user/libs/libc/malloc.c
  6. 3 3
      user/libs/libc/unistd.h

+ 187 - 48
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)
+    {
+        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
     {
-        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 = 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;
 }

+ 1 - 1
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) // 当前进程运行时间大于了下一进程的运行时间,进行切换
     {
 

+ 32 - 9
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);
+
+    /*
+    if (offset < 0)
+    {
+        kdebug("decrease brk, offset = %#010lx", (uint64_t)(-offset));
+    }
+    */
 
-    new_brk = mm_do_brk(current_pcb->mm->brk_end, new_brk - current_pcb->mm->brk_end); // 扩展堆内存空间
+    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; // 返回码
 }

+ 4 - 3
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)

+ 84 - 24
user/libs/libc/malloc.c

@@ -5,6 +5,24 @@
 #include <libc/errno.h>
 #include <libc/stdio.h>
 
+#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();
+    }
 }

+ 3 - 3
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);