Linux系统编程:(3)进程内存分配

一、堆内存分配

进程在运行期间可以通过增加堆(heap)的大小来为进程分配内存,在进程的内存布局中,堆是一段大小可变的连续虚拟内存,始于BSS段的末尾,随着内存的分配和释放而增减。和栈有自己的指针指向栈顶一样,堆也有指向堆边界的“program break”。
Linux系统编程:(3)进程内存分配_第1张图片

1.调整program break:brk()和sbrk()
改变堆的大小(即分配或释放内存),其实就像命令内核改变program break位置一样简单,最初,program break与BSS段末尾重合,在program break抬升后,程序可以访问新增内存的任何地址,而此时物理内存页尚未分配,在首次访问该内存时,引发页面错误后会自动分配内存物理页

这里有个问题,上面为什么没有说是调用malloc函数后program break抬升呢?因为,malloc调用后,program break不一定改变,这个就要从malloc和free说起,因为free掉一块内存时,内核大多时候会将其保存在一个空闲内存列表中,后续再有malloc来申请内存,先看空闲内存列表中有没有满足的内存,如果有就直接分出去,如果没有,就在去堆新申请,然后program break就改变了。

两个操纵program break的系统调用:

int brk(void *end_date_segment);
void *sbrk(intptr_t increment);
  • brk()会将program break设置为参数end_data_segment所指定的位置,以页为整体单位。
  • sbrk()将program break在原有地址上增加从参数increment传入的大小,返回前一个的program break,即新分配内存的起始地址的指针。strk(0)返回当前program break位置,对其不改变,可以用来跟踪堆的大小或者堆的变化行为。

2.内存申请和释放:malloc()和free()
free()在一般情况下,不会降低program break的位置,而是将这块内存存放到空闲的内存列表中,供后续malloc函数循环调用。这么做原因如下:
(1)被释放的内存位于堆中间,而非堆顶部,降低program break是不可能的
(2)可以最大限度减少程序必须执行的sbrk()调用次数,系统调用的开销也是很客观的
(3)大多数情况,降低program break的位置对那些分配大量内存的程序不会有帮助,因为他们通常倾向于持有已有分配内存或是反复释放和重新分配内存,而非释放所有内存后再持续运行一段时间。

3.free如何知道要释放内存的大小?
malloc在申请内存,系统给你分配内存的时候,会给你额外分配几个字节来记录这块内存大小的整数值,该区域在内存块的起始处,而返回给调用者的内存地址恰好处于这一长度记录的字节后。
Linux系统编程:(3)进程内存分配_第2张图片
4.堆上其他分配内存方法:calloc和realloc

二、栈内存分配

void alloca(size_t size);

alloca可以从栈上面通过增加栈帧的方式动态分配内存,不需要使用free来释放由alloca分配的内存,是由堆栈在返回返回时自动释放。若因为分配内存造成的栈溢出,则行为是未定义的。

内存分配原理的好文章:http://abcdxyzk.github.io/blog/2015/08/05/kernel-mm-malloc/

你可能感兴趣的:(Linux系统编程)