malloc.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. // Copyright (C) DragonOS Community longjin
  2. // This program is free software; you can redistribute it and/or
  3. // modify it under the terms of the GNU General Public License
  4. // as published by the Free Software Foundation; either version 2
  5. // of the License, or (at your option) any later version.
  6. // This program is distributed in the hope that it will be useful,
  7. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. // GNU General Public License for more details.
  10. // You should have received a copy of the GNU General Public License
  11. // along with this program; if not, write to the Free Software
  12. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  13. // Or you can visit https://www.gnu.org/licenses/gpl-2.0.html
  14. #include <unistd.h>
  15. #include <errno.h>
  16. #include <stdio.h>
  17. #include <stdint.h>
  18. #define PAGE_4K_SHIFT 12
  19. #define PAGE_2M_SHIFT 21
  20. #define PAGE_1G_SHIFT 30
  21. #define PAGE_GDT_SHIFT 39
  22. /***************************/
  23. // 不同大小的页的容量
  24. #define PAGE_4K_SIZE (1UL << PAGE_4K_SHIFT)
  25. #define PAGE_2M_SIZE (1UL << PAGE_2M_SHIFT)
  26. #define PAGE_1G_SIZE (1UL << PAGE_1G_SHIFT)
  27. // 屏蔽低于x的数值
  28. #define PAGE_4K_MASK (~(PAGE_4K_SIZE - 1))
  29. #define PAGE_2M_MASK (~(PAGE_2M_SIZE - 1))
  30. // 将addr按照x的上边界对齐
  31. //#define PAGE_4K_ALIGN(addr) (((unsigned long)(addr) + PAGE_4K_SIZE - 1) & PAGE_4K_MASK)
  32. //#define PAGE_2M_ALIGN(addr) (((unsigned long)(addr) + PAGE_2M_SIZE - 1) & PAGE_2M_MASK)
  33. /**
  34. * @brief 显式链表的结点
  35. *
  36. */
  37. typedef struct malloc_mem_chunk_t
  38. {
  39. uint64_t length; // 整个块所占用的内存区域的大小
  40. uint64_t padding;
  41. struct malloc_mem_chunk_t *prev; // 上一个结点的指针
  42. struct malloc_mem_chunk_t *next; // 下一个结点的指针
  43. } malloc_mem_chunk_t;
  44. static uint64_t brk_base_addr = 0; // 堆区域的内存基地址
  45. static uint64_t brk_max_addr = 0; // 堆区域的内存最大地址
  46. static uint64_t brk_managed_addr = 0; // 堆区域已经被管理的地址
  47. // 空闲链表
  48. // 按start_addr升序排序
  49. static malloc_mem_chunk_t *malloc_free_list = NULL;
  50. static malloc_mem_chunk_t *malloc_free_list_end = NULL; // 空闲链表的末尾结点
  51. static uint64_t count_last_free_size = 0; // 统计距离上一次回收内存,已经free了多少内存
  52. /**
  53. * @brief 将块插入空闲链表
  54. *
  55. * @param ck 待插入的块
  56. */
  57. static void malloc_insert_free_list(malloc_mem_chunk_t *ck);
  58. /**
  59. * @brief 当堆顶空闲空间大于2个页的空间的时候,释放1个页
  60. *
  61. */
  62. static void release_brk();
  63. /**
  64. * @brief 在链表中检索符合要求的空闲块(best fit)
  65. *
  66. * @param size 块的大小
  67. * @return malloc_mem_chunk_t*
  68. */
  69. static malloc_mem_chunk_t *malloc_query_free_chunk_bf(uint64_t size)
  70. {
  71. // 在满足best fit的前提下,尽可能的使分配的内存在低地址
  72. // 使得总的堆内存可以更快被释放
  73. if (malloc_free_list == NULL)
  74. {
  75. return NULL;
  76. }
  77. malloc_mem_chunk_t *ptr = malloc_free_list;
  78. malloc_mem_chunk_t *best = NULL;
  79. // printf("query size=%d", size);
  80. while (ptr != NULL)
  81. {
  82. // printf("ptr->length=%#010lx\n", ptr->length);
  83. if (ptr->length == size)
  84. {
  85. best = ptr;
  86. break;
  87. }
  88. if (ptr->length > size)
  89. {
  90. if (best == NULL)
  91. best = ptr;
  92. else if (best->length > ptr->length)
  93. best = ptr;
  94. }
  95. ptr = ptr->next;
  96. }
  97. return best;
  98. }
  99. /**
  100. * @brief 在链表中检索符合要求的空闲块(first fit)
  101. *
  102. * @param size
  103. * @return malloc_mem_chunk_t*
  104. */
  105. static malloc_mem_chunk_t *malloc_query_free_chunk_ff(uint64_t size)
  106. {
  107. if (malloc_free_list == NULL)
  108. return NULL;
  109. malloc_mem_chunk_t *ptr = malloc_free_list;
  110. while (ptr)
  111. {
  112. if (ptr->length >= size)
  113. {
  114. return ptr;
  115. }
  116. ptr = ptr->next;
  117. }
  118. return NULL;
  119. }
  120. /**
  121. * @brief 扩容malloc管理的内存区域
  122. *
  123. * @param size 扩大的内存大小
  124. */
  125. static int malloc_enlarge(int64_t size)
  126. {
  127. if (brk_base_addr == 0) // 第一次调用,需要初始化
  128. {
  129. brk_base_addr = sbrk(0);
  130. // printf("brk_base_addr=%#018lx\n", brk_base_addr);
  131. brk_managed_addr = brk_base_addr;
  132. brk_max_addr = sbrk(0);
  133. }
  134. int64_t free_space = brk_max_addr - brk_managed_addr;
  135. // printf("size=%ld\tfree_space=%ld\n", size, free_space);
  136. if (free_space < size) // 现有堆空间不足
  137. {
  138. if (sbrk(size - free_space) != (void *)(-1))
  139. brk_max_addr = sbrk((0));
  140. else
  141. {
  142. //put_string("malloc_enlarge(): no_mem\n", COLOR_YELLOW, COLOR_BLACK);
  143. return -ENOMEM;
  144. }
  145. // printf("brk max addr = %#018lx\n", brk_max_addr);
  146. }
  147. // 扩展管理的堆空间
  148. // 在新分配的内存的底部放置header
  149. // printf("managed addr = %#018lx\n", brk_managed_addr);
  150. malloc_mem_chunk_t *new_ck = (malloc_mem_chunk_t *)brk_managed_addr;
  151. new_ck->length = brk_max_addr - brk_managed_addr;
  152. // 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);
  153. new_ck->prev = NULL;
  154. new_ck->next = NULL;
  155. brk_managed_addr = brk_max_addr;
  156. malloc_insert_free_list(new_ck);
  157. return 0;
  158. }
  159. /**
  160. * @brief 合并空闲块
  161. *
  162. */
  163. static void malloc_merge_free_chunk()
  164. {
  165. if (malloc_free_list == NULL)
  166. return;
  167. malloc_mem_chunk_t *ptr = malloc_free_list->next;
  168. while (ptr != NULL)
  169. {
  170. // 内存块连续
  171. if (((uint64_t)(ptr->prev) + ptr->prev->length == (uint64_t)ptr))
  172. {
  173. // printf("merged %#018lx and %#018lx\n", (uint64_t)ptr, (uint64_t)(ptr->prev));
  174. // 将ptr与前面的空闲块合并
  175. ptr->prev->length += ptr->length;
  176. ptr->prev->next = ptr->next;
  177. if (ptr->next == NULL)
  178. malloc_free_list_end = ptr->prev;
  179. else
  180. ptr->next->prev = ptr->prev;
  181. // 由于内存组成结构的原因,不需要free掉header
  182. ptr = ptr->prev;
  183. }
  184. ptr = ptr->next;
  185. }
  186. }
  187. /**
  188. * @brief 将块插入空闲链表
  189. *
  190. * @param ck 待插入的块
  191. */
  192. static void malloc_insert_free_list(malloc_mem_chunk_t *ck)
  193. {
  194. if (malloc_free_list == NULL) // 空闲链表为空
  195. {
  196. malloc_free_list = ck;
  197. malloc_free_list_end = ck;
  198. ck->prev = ck->next = NULL;
  199. return;
  200. }
  201. else
  202. {
  203. malloc_mem_chunk_t *ptr = malloc_free_list;
  204. while (ptr != NULL)
  205. {
  206. if ((uint64_t)ptr < (uint64_t)ck)
  207. {
  208. if (ptr->next == NULL) // 当前是最后一个项
  209. {
  210. ptr->next = ck;
  211. ck->next = NULL;
  212. ck->prev = ptr;
  213. malloc_free_list_end = ck;
  214. break;
  215. }
  216. else if ((uint64_t)(ptr->next) > (uint64_t)ck)
  217. {
  218. ck->prev = ptr;
  219. ck->next = ptr->next;
  220. ptr->next = ck;
  221. ck->next->prev = ck;
  222. break;
  223. }
  224. }
  225. else // 在ptr之前插入
  226. {
  227. if (ptr->prev == NULL) // 是第一个项
  228. {
  229. malloc_free_list = ck;
  230. ck->prev = NULL;
  231. ck->next = ptr;
  232. ptr->prev = ck;
  233. break;
  234. }
  235. else
  236. {
  237. ck->prev = ptr->prev;
  238. ck->next = ptr;
  239. ck->prev->next = ck;
  240. ptr->prev = ck;
  241. break;
  242. }
  243. }
  244. ptr = ptr->next;
  245. }
  246. }
  247. }
  248. /**
  249. * @brief 获取一块堆内存
  250. *
  251. * @param size 内存大小
  252. * @return void* 内存空间的指针
  253. *
  254. * 分配内存的时候,结点的prev next指针所占用的空间被当做空闲空间分配出去
  255. */
  256. void *_dragonos_malloc(ssize_t size)
  257. {
  258. // 计算需要分配的块的大小
  259. // reserve for len
  260. if (size + 2*sizeof(uint64_t) <= sizeof(malloc_mem_chunk_t))
  261. size = sizeof(malloc_mem_chunk_t);
  262. else
  263. size += 2*sizeof(uint64_t);
  264. // 采用best fit
  265. malloc_mem_chunk_t *ck = malloc_query_free_chunk_bf(size);
  266. if (ck == NULL) // 没有空闲块
  267. {
  268. // printf("no free blocks\n");
  269. // 尝试合并空闲块
  270. malloc_merge_free_chunk();
  271. ck = malloc_query_free_chunk_bf(size);
  272. // 找到了合适的块
  273. if (ck)
  274. goto found;
  275. // printf("before enlarge\n");
  276. // 找不到合适的块,扩容堆区域
  277. if (malloc_enlarge(size) == -ENOMEM)
  278. return (void *)-ENOMEM; // 内存不足
  279. malloc_merge_free_chunk(); // 扩容后运行合并,否则会导致碎片
  280. // 扩容后再次尝试获取
  281. ck = malloc_query_free_chunk_bf(size);
  282. }
  283. found:;
  284. // printf("ck = %#018lx\n", (uint64_t)ck);
  285. if (ck == NULL)
  286. return (void *)-ENOMEM;
  287. // printf("ck->prev=%#018lx ck->next=%#018lx\n", ck->prev, ck->next);
  288. // 分配空闲块
  289. // 从空闲链表取出
  290. if (ck->prev == NULL) // 当前是链表的第一个块
  291. {
  292. malloc_free_list = ck->next;
  293. }
  294. else
  295. ck->prev->next = ck->next;
  296. if (ck->next != NULL) // 当前不是最后一个块
  297. ck->next->prev = ck->prev;
  298. else
  299. malloc_free_list_end = ck->prev;
  300. // 当前块剩余的空间还能容纳多一个结点的空间,则分裂当前块
  301. if ((int64_t)(ck->length) - size > sizeof(malloc_mem_chunk_t))
  302. {
  303. // printf("seperate\n");
  304. malloc_mem_chunk_t *new_ck = (malloc_mem_chunk_t *)(((uint64_t)ck) + size);
  305. new_ck->length = ck->length - size;
  306. new_ck->prev = new_ck->next = NULL;
  307. // printf("new_ck=%#018lx, new_ck->length=%#010lx\n", (uint64_t)new_ck, new_ck->length);
  308. ck->length = size;
  309. malloc_insert_free_list(new_ck);
  310. }
  311. // printf("malloc done: %#018lx, length=%#018lx\n", ((uint64_t)ck + sizeof(uint64_t)), ck->length);
  312. // 此时链表结点的指针的空间被分配出去
  313. return (void *)((uint64_t)ck + sizeof(uint64_t));
  314. }
  315. /**
  316. * @brief 当堆顶空闲空间大于2个页的空间的时候,释放1个页
  317. *
  318. */
  319. static void release_brk()
  320. {
  321. // 先检测最顶上的块
  322. // 由于块按照开始地址排列,因此找最后一个块
  323. if (malloc_free_list_end == NULL)
  324. {
  325. printf("release(): free list end is null. \n");
  326. return;
  327. }
  328. 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))
  329. {
  330. int64_t delta = ((brk_max_addr - (uint64_t)malloc_free_list_end) & PAGE_2M_MASK) - PAGE_2M_SIZE;
  331. // printf("(brk_max_addr - (uint64_t)malloc_free_list_end) & PAGE_2M_MASK=%#018lx\n ", (brk_max_addr - (uint64_t)malloc_free_list_end) & PAGE_2M_MASK);
  332. // printf("PAGE_2M_SIZE=%#018lx\n", PAGE_2M_SIZE);
  333. // printf("tdfghgbdfggkmfn=%#018lx\n ", (brk_max_addr - (uint64_t)malloc_free_list_end) & PAGE_2M_MASK - PAGE_2M_SIZE);
  334. // printf("delta=%#018lx\n ", delta);
  335. if (delta <= 0) // 不用释放内存
  336. return;
  337. sbrk(-delta);
  338. brk_max_addr = sbrk(0);
  339. brk_managed_addr = brk_max_addr;
  340. malloc_free_list_end->length = brk_max_addr - (uint64_t)malloc_free_list_end;
  341. }
  342. }
  343. /**
  344. * @brief 释放一块堆内存
  345. *
  346. * @param ptr 堆内存的指针
  347. */
  348. void _dragonos_free(void *ptr)
  349. {
  350. // 找到结点(此时prev和next都处于未初始化的状态)
  351. malloc_mem_chunk_t *ck = (malloc_mem_chunk_t *)((uint64_t)ptr - sizeof(uint64_t));
  352. // printf("free(): addr = %#018lx\t len=%#018lx\n", (uint64_t)ck, ck->length);
  353. count_last_free_size += ck->length;
  354. malloc_insert_free_list(ck);
  355. if (count_last_free_size > PAGE_2M_SIZE)
  356. {
  357. count_last_free_size = 0;
  358. malloc_merge_free_chunk();
  359. release_brk();
  360. }
  361. }