TL;DR
- 在读这篇文章的时候你应该知道c语言的malloc是使用系统调用从操作系统申请内存的,这两个系统调用分别是brk和mmap。
/prco/$pid/maps文件
- 先来简单看下这个文件,这个文件会显示整个进程的地址空间分布情况。
brk
- brk从内核获取内存是通过增加程序中断地址方式的,开始于start_brk,结束于brk,初始的时候两者都指向的是同一个位置。
- 当ASLR关闭的时候,start_brk和brk都是指向bss段的尾部的
- 当ASLR开启的时候,start_brk和brk初始位置是bss段的尾部加一个随机的偏移。
nice pic,a ha :D - 如上图虚拟内存地址空间分布图所示,start_brk即是堆空间的开始,brk即是堆空间的结束。
!注意,后面的测试均是关闭ASLR后测试输出,此外测试系统使用的是ubuntu14.04.1X64操作系统。
1
2关闭ASLR的方法
#echo 0 > /proc/sys/kernel/randomize_va_space示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31/* sbrk and brk example */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
void *curr_brk, *tmp_brk = NULL;
printf("Welcome to sbrk example:%d\n", getpid());
/* sbrk(0) gives current program break location */
tmp_brk = curr_brk = sbrk(0);
printf("Program Break Location1:%p\n", curr_brk);
getchar();
/* brk(addr) increments/decrements program break location */
brk(curr_brk+4096);
curr_brk = sbrk(0);
printf("Program break Location2:%p\n", curr_brk);
getchar();
brk(tmp_brk);
curr_brk = sbrk(0);
printf("Program Break Location3:%p\n", curr_brk);
getchar();
return 0;
}输出分析:
- 在brk之前,我们可以看到进程中是没有堆块的,因此 start_brk=brk=end_data=0x602000
- 当程序执行brk调用的时候,如下的输出,我们看到有了堆块,因此 start_brk=end_data=0x602000,此时
- start_brk=end_data=0x602000
- brk = 0x603000
- 解释一下新增加的一行:这里602000-603000就是动态分配的地址空间,对应的操作权限标志是rw-p即可读可写不可执行,私有的
- 000000的文件偏移是因为没有映射任何的文件,00:00 是主要/次要的设备数-由于没有映射任何文件,所以这里也是0,最后的0是inode number,还是由于没有映射任何的文件,所以这里也还是0.
mmap
malloc使用mmap来创建一个私有的匿名映射块,私有的匿名映射块主要的目的就是分配新的内存。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36/* Private anonymous mapping example using mmap syscall */
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
void static inline errExit(const char* msg)
{
printf("%s failed. Exiting the process\n", msg);
exit(-1);
}
int main()
{
int ret = -1;
printf("Welcome to private anonymous mapping example::PID:%d\n", getpid());
printf("Before mmap\n");
getchar();
char* addr = NULL;
addr = mmap(NULL, (size_t)132*1024, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr == MAP_FAILED)
errExit("mmap");
printf("After mmap\n");
getchar();
/* Unmap mapped region. */
ret = munmap(addr, (size_t)132*1024);
if(ret == -1)
errExit("munmap");
printf("After munmap\n");
getchar();
return 0;
}输出分析:
- 在使用mmap之前:我们注意下面的一个内存地址段
- 当我们通过mmap申请了132KB的空间的时候,看到如下图
b7df0000-b7e12000 其中这段虚拟内存地址就包含了我们刚刚申请到132KB大小的地址空间 - 其中b7df0000-b7e12000 是这个块的地址范围,对应的操作权限标志是rw-p即可读可写不可执行,私有的
- 000000的文件偏移是因为没有映射任何的文件,00:00 是主要/次要的设备数-由于没有映射任何文件,所以这里也是0,最后的0是inode number,还是由于没有映射任何的文件,所以这里也还是0.
- 当munmap之后,下面的输出我们可以看的到申请的内存被释放(变成原来的b7e11000-b7e12000),又交还给了操作系统。
其它
- 在64位上brk和32的结果是相同的,但是mmap的会有不同,原因暂时不明。
reference
- Syscalls used by malloc.