Linux 进程地址空间 堆的管理

当进程被创建时,就预留了一块特殊的线性区,其开始地址和结束地址单独保存在 mm_struct.start_brkmm_strcut.brk成员中,并不由vm_area_struct 链接,这块特殊的线性区就叫堆。

进程使用的malloc()free()等相关 API 都是操纵的堆空间。

修改堆空间的接口

对用户态进程来说,提供brk()系统调用来修改自身的堆空间。

brk(): 参数addr, 效果是修改mm_struct.brk到 addr,即修改一个堆的结束地址。

brk() 系统调用的实现,在内核态是调用do_mmap()扩充堆,或者do_unmap()缩小堆。 并且移动mm_struct.brk的值而已,这是 brk()的实现。

用户态进程还有一个接口: sbrk(), 参数是字节,代表扩充的字节数。 其下层还是调用的 brk()

malloc()的实现

进程刚创建时,堆空间的大小为 0, 即bkr==start_brk

调用malloc(),即对堆空间扩充,上面介绍了修改堆空间的接口, 所以我们可以使用brk()来实现malloc().

对于进程本身来说,只能通过brk()简单的增加/减少堆的总大小,这样做的效率是比较低的。 比如连续执行了三次malloc(), 如果要将中间的地址 free 掉,其实是无法实现的。

而且这种最简单的情况下,每次malloc()都要使用brk()系统调用,效率也是很低的。

所以,通常在 C 库则一层,即malloc()brk()之间,会有一层对堆内存的管理, 包含碎片回收,内存池等算法来避免频繁的使用系统调用。


创建于: 2023-05-08T10:51:49, Lastmod: 2023-09-24T18:08:59