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()
之间,会有一层对堆内存的管理,
包含碎片回收,内存池等算法来避免频繁的使用系统调用。