每当您研究进程的内存分配时,您通常会看到它的概述如下:
到目前为止,一切都很好。
但是您有 sbrk() 系统调用,它允许程序更改其数据部分的上限,并且它也可以用于简单地检查 sbrk(0) 的限制在哪里。使用该功能,我发现了以下模式:
模式 1 - 小 malloc
我在我的 Linux 机器上运行以下程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int globalVar;
int main(){
int localVar;
int *ptr;
printf("localVar address (i.e., stack) = %p\n",&localVar);
printf("globalVar address (i.e., data section) = %p\n",&globalVar);
printf("Limit of data section = %p\n",sbrk(0));
ptr = malloc(sizeof(int)*1000);
printf("ptr address (should be on stack)= %p\n",&ptr);
printf("ptr points to: %p\n",ptr);
printf("Limit of data section after malloc= %p\n",sbrk(0));
return 0;
}
输出如下:
localVar address (i.e., stack) = 0xbfe34058
globalVar address (i.e., data section) = 0x804a024
Limit of data section = 0x91d9000
ptr address (should be on stack)= 0xbfe3405c
ptr points to: 0x91d9008
Limit of data section after malloc= 0x91fa000
如您所见,分配的内存区域正好在旧数据段限制之上,并且在 malloc 之后该限制被向上推,因此分配的区域实际上位于新数据段内。
问题1:这是否意味着小型mallocs将在数据部分分配内存而根本不使用堆?
模式 2 - 大马洛克
如果在第 15 行增加请求的内存大小:
ptr = malloc(sizeof(int)*100000);
您现在将得到以下输出:
localVar address (i.e., stack) = 0xbf93ba68
globalVar address (i.e., data section) = 0x804a024
Limit of data section = 0x8b16000
ptr address (should be on stack)= 0xbf93ba6c
ptr points to: 0xb750b008
Limit of data section after malloc= 0x8b16000
正如您在此处看到的,数据段的限制没有改变,而是分配的内存区域位于间隙段的中间,在数据段和堆栈之间。
问题 2:这实际上是使用堆的大型 malloc 吗?
问题3:对这种行为有什么解释吗?我发现它有点不安全,因为在第一个示例(小 malloc)中,即使在您释放分配的内存之后,您仍然可以使用指针并使用该内存而不会出现段错误,因为它将在您的数据中部分,这可能导致难以检测到错误。
更新规格:Ubuntu 12.04,32 位,gcc 版本 4.6.3,Linux 内核 3.2.0-54-generic-pae。
更新 2:罗德里戈的回答解决了这个谜团。这个维基百科链接也有帮助。