Linux 进程地址空间 堆的管理
当进程被创建时,就预留了一块特殊的线性区,其开始地址和结束地址单独保存在
mm_struct.start_brk和mm_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()之间,会有一层对堆内存的管理,
包含碎片回收,内存池等算法来避免频繁的使用系统调用。