原文:Hack the Virtual Memory: malloc, the heap & the program break - Holberton

翻译:RobotCode俱乐部

The heap(堆)

在这一章中,我们将研究堆和malloc,以回答我们在前一章结尾时提出的一些问题:

  • 为什么我们分配的内存不从堆的最开始(0x2050010 vs 02050000)开始呢?前8个位元组的用途是什么?
  • 堆实际上是向上增长的吗?

malloc

malloc是用于动态分配内存的常用函数。这个内存是在「堆」上分配的。

NOTE:malloc不是一个系统调用。

No malloc, no [heap]

让我们看看不调用malloc 的进程的内存区域。

#include <stdlib.h>
#include <stdio.h>

/**
* main - do nothing
*
* Return: EXIT_FAILURE if something failed. Otherwise EXIT_SUCCESS
*/
int main(void)
{
getchar();
return (EXIT_SUCCESS);
}

从上面的映射文件中可以看到,没有分配[heap]区域。

malloc(x)

让我们用一个调用malloc (1-main.c)的程序来做同样的事情:

#include <stdio.h>
#include <stdlib.h>

/**
* main - 1 call to malloc
*
* Return: EXIT_FAILURE if something failed. Otherwise EXIT_SUCCESS
*/
int main(void)
{
malloc(1);
getchar();
return (EXIT_SUCCESS);
}

让我们检查malloc的返回值,以确保返回的地址位于堆区域(2-main.c):

#include <stdio.h>
#include <stdlib.h>

/**
* main - prints the malloc returned address
*
* Return: EXIT_FAILURE if something failed. Otherwise EXIT_SUCCESS
*/
int main(void)
{
void *p;

p = malloc(1);
printf("%p
", p);
getchar();
return (EXIT_SUCCESS);
}

返回的地址在堆区域内。正如我们在前一章中所看到的,返回的地址并不完全从区域的开头开始;我们待会再看原因。

strace, brk and sbrk

malloc是一个「常规」函数(与系统调用相反),因此它必须调用某种类型的syscall来操作堆。让我们用strace来找出答案。

strace是一个用于跟踪系统调用和信号的程序。任何程序在执行主函数之前都会使用一些系统调用。为了知道malloc使用哪些系统调用,我们将在调用malloc之前和之后添加一个write系统调用。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/**
* main - lets find out which syscall malloc is using
*
* Return: EXIT_FAILURE if something failed. Otherwise EXIT_SUCCESS
*/
int main(void)
{
void *p;

write(1, "BEFORE MALLOC
", 14);
p = malloc(1);
write(1, "AFTER MALLOC
", 13);
printf("%p
", p);
getchar();
return (EXIT_SUCCESS);
}

malloc使用brk系统调用来操作堆。从brk手册页(man brk),我们可以看到这个系统调用正在做什么:

程序中断是虚拟内存中超过程序数据区域当前结束位置的第一个位置的地址,如下图所示:

通过增加程序断点的值,通过brk或sbrk, malloc函数创建一个新的空间,然后进程可以使用这个空间来动态分配内存(使用malloc),如下图:

堆实际上是程序数据段的扩展。

对brk(brk(0))的第一个调用将程序断点的当前地址返回给malloc。第二个调用是通过增加程序断点的值实际创建新内存的调用(从0x8a33000- 0x8a54000)。在上面的示例中,堆现在从0x8a33000开始,结束于0x8a54000。让我们再次检查/proc/[PID]/maps文件:

maps文件里的输出完全匹配brk返回给malloc的指针。

这很好,但是等等,当我们只要求一个位元组时,为什么malloc将堆增加0x8a33000- 0x8a54000=0x21000或135168位元组呢?

这个问题下篇讨论,未完待续。

由于本人水平有限,翻译必然有很多不妥的地方,欢迎指正。

同时,欢迎关注下方微信公众号,一起交流学习:)


推荐阅读:
相关文章