接前一篇文章:Linux内核有什么之内存管理子系统有什么第二回 —— 单刀直入
本文内容参考:
内存分配不再神秘:深入剖析malloc函数实现原理与机制
系统调用与内存管理(sbrk、brk、mmap、munmap)
特此致谢!
二、小内存分配 —— brk与sbrk
上回讲到通过malloc()申请一个堆内的空间,底层要么执行brk(小内存分配),要么执行mmap(大内存分配)。二者之间的界限是MMAP_THRESHOLD(默认128KB)。小于等于MMAP_THRESHOLD的使用brk,大于MMAP_THRESHOLD的使用mmap。
先来看小内存分配。上一回中man malloc得到的说明中实际上提到了两个相关的系统调用:brk与sbrk。依旧在Linux终端下通过man brk查看其说明,如下所示:
两个系统调用的说明如下:
名称
brk,sbrk
原型
#include <unistd.h>
int brk(void *addr);
void *sbrk(intptr_t increment);
说明
brk()和sbrk()改变程序间断点的位置,程序间断点就是进程数据段的结尾。(例如,程序间断点是未初始化数据段的结束之后的首个位置)。增加程序间断点具有为进程分配内存的效果;减少程序间断点则释放内存。
当addr参数合理、系统有足够的内存并且不超过最大值时,brk()函数将数据段结尾设置为addr,即间断点设置为addr(请参阅setrlimit(2))。
sbrk()将程序数据空间增加increment字节。当increment为0时则是返回程序间断点的当前位置。
brk()将break指针直接设置为某个地址,而sbrk()将break指针从当前位置移动increment所指定的增量。
返回值
brk()成功时返回0,失败返回-1并且设置errno值为ENOMEM。
sbrk()成功时返回之前的程序间断点地址。如果间断点值增加,那么这个指针(指的是返回的之前的间断点地址)是指向分配的新的内存的首地址。如果出错失败,就返回一个(void *) -1
并设置errno全局变量的值为ENOMEM。
注
避免使用brk()和sbrk():malloc(3)内存分配包是一种可移植且舒适的内存分配方式。
总结
brk()和sbrk()都用来改变 “program break”(程序间断点)的位置,改变数据段长度(Change data segment size),实现虚拟内存到物理内存的映射。
- brk()直接修改有效访问范围的末尾地址实现分配与回收。
- sbrk()函数当参数increment为正值时,程序间断点位置向后移动increment字节,同时返回移动之前的位置,相当于分配内存;当increment为负值时,位置向前移动increment字节,相当与于释放内存,其返回值没有实际意义;当increment为0时,不移动位置只返回当前位置。参数increment的符号决定了是分配还是回收内存。
下一回开始结合代码,对于brk()和sbrk()进行详解。