选自:
调用malloc时发生了什么
Linux内存分配小结--malloc、brk、mmap
-
brk() and sbrk() change the location of the program break, which defines the end of the process's data segment (i.e., the program break is the first location after the end of the uninitialized data segment).
即,这两个函数都是用来改变 "program break"(堆尾)的位置。二者主要功能是实现了实现虚拟内存到内存的映射。
brk()
1.
brk()是系统调用,事实上,
malloc()是C标准库函数,其实际对应的系统调用就是brk()函数(实际上是syscall 1)。
2.brk()的入参是指定堆的结束地址,是绝对地址;
brk()返回0表示成功;返回-1表示失败,一般指的是没有内存了。
3.在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk,mmap/munmap这些系统调用实现的。
step1:当malloc申请小于128k的内存时,使用brk()分配内存,将"堆结束地址"往高地址推(只分配虚拟空间,不对应物理内存,因此没有初始化)。事实上brk()只是完成虚拟地址的分配,而这块内存现在还是没有物理页与之对应的,等到进程第一次读写这块内存的时候,发生缺页中断,这个时候,内核才分配这块内存对应的物理页,与虚拟地址空间建立映射关系。
step2:当malloc申请大于128k的内存时,使用mmap分配内存,在堆和栈之间找一块空闲内存分配(对应独立内存,而且初始化为0)。
这样做的原因:
brk分配的内存需要等到高地址内存释放以后才能释放(例如,先A = malloc(30K);再B = malloc(40K);那么在B释放之前,A是不可能释放的(因为只有一个"堆结束指针"呀),这就是内存碎片产生的原因),当最高地址空间的空闲内存超过128K(可由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim),
而mmap分配的内存可以单独释放。然而,
既然堆内内存brk(以及sbrk)不能直接释放,为什么不全部使用 mmap来分配,munmap直接释放呢?mmap申请的内存被munmap后,重新申请会产生更多的缺页中断,缺页中断是内核行为,会导致内核态CPU消耗较大。而使用brk分配的内存即使释放了,也没有归还给OS, 再次访问该内存很可能不需产生任何系统调用和缺页中断,这将大大降低CPU的消耗。因此, glibc 的 malloc 实现中,充分考虑了brk和mmap行为上的差异及优缺点。
sbrk()
1.
sbrk()是Linux系统函数,头文件是unistd.h。
2.sbrk()的入参是指定堆的结束地址,是相对地址;
sbrk()的返回值新设置的堆结束地址(这里是绝对地址)。如:sbrk(0)获取当前的堆的结束地址。
3.malloc与sbrk的关系:“gcc中的malloc就没调用sbrk”
实例:
-
#include <unistd.h>
-
#include <stdio.h>
-
-
void main()
-
{
-
char *brk_end = NULL;
-
/*表示自己想扩展堆大小0字节
-
*由于sbrk返回的是新的brk_end,所以sbrk(0)就能获取到当前
-
*的brk_end
-
*/
-
char *p = sbrk(0);
-
printf("current brk end:%p\n",p);
-
/*brk的入参是绝对地址,表示自己想要拓展brk_end至p+4096*/
-
brk(p+4096);
-
/*再次尝试获取当前的brk_end*/
-
brk_end = sbrk(0);
-
printf("current brk end:%p\n",brk_end);
-
}
输出:
点击(此处)折叠或打开
-
current brk end:0x98e000
-
current brk end:0x98f000
阅读(4899) | 评论(0) | 转发(0) |