当 sbrk() 返回指向堆开始地址的指针时,地址是升序还是降序?例如,如果我有一个从地址 1 到 10 的 10 字节堆,那么 sbrk() 会返回指向地址 10 还是 1 的指针?
类似地,堆地址趋于“向下”增长......但我如何才能确定我的计算机上的地址是增加还是减少?
当 sbrk() 返回指向堆开始地址的指针时,地址是升序还是降序?例如,如果我有一个从地址 1 到 10 的 10 字节堆,那么 sbrk() 会返回指向地址 10 还是 1 的指针?
类似地,堆地址趋于“向下”增长......但我如何才能确定我的计算机上的地址是增加还是减少?
Mac OS X 上的手册页说:
brk
和sbrk
函数是在虚拟内存管理出现之前的早期遗留下来的历史奇闻。程序中断的当前值由 可靠地返回
sbrk(0)
。如果成功,该
sbrk
函数返回一个指向新存储基址的指针;否则 -1 并errno
设置为指示分配失败的原因。
假设您使用:
void *base = sbrk(1024);
之后,假设没有错误,base
将包含 1024 字节(最小)内存块的起始地址;(char *)base + 1024
将超出您的要求,尽管它可能仍然有效,因为页面大小可能大于 1024。
它没有直接说明后续分配是否将具有比另一个更大或更小的地址。但是,它可能是按地址升序排列的。
该
brk()
函数将进程数据段(未初始化的数据)的断点或最低地址设置为 addr(紧接在 bss 上方)。数据寻址被限制在addr
堆栈段的最低堆栈指针和最低堆栈指针之间。内存是按brk
页面大小的块分配的;如果addr
不能被系统页面大小整除,则增加到下一个页面边界。
这意味着额外的空间在 data 和 bss 段之后,并且朝着堆栈增长(在内存中向下增长)。但是,依靠它可能会很鲁莽。调用后最好使用sbrk(0)
建立当前端sbrk(extra)
来获得额外空间;这将告诉你你真正得到了什么,两个地址告诉你它在哪里可用。
如果您在 Linux 上,您可以检查/proc/$PID/maps
虚拟地址空间是如何用于每个进程的。
示例代码:mappingTest.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int foo(int depth) {
char buf[8192];
if (0 == depth)
printf("%p\n", buf);
if (1000 < depth) {
printf("%p\n", buf);
return getchar();
} else {
return 1 + foo(depth + 1);
}
}
int main() {
const size_t SIZE = 1000 * 1000 * 1000;
getchar();
char * const p = malloc(SIZE);
printf("%p\n", p);
getchar();
free(p);
getchar();
foo(0);
return 0;
}
注意:时间char buf[8192]
递归1000
假设最大堆栈大小为 8 MB(您可以使用ulimit -s
.)确认。
$ gcc mappingTest.c -o mappingTest -Wall -Wextra -Wno-missing-field-initializers -std=c89 -O0 -g3 && echo OK
OK
$ ./mappingTest
在第 1 处getchar()
,我们看到进程的以下内存映射:
$ cat /proc/`pidof mappingTest`/maps
00400000-00401000 r-xp 00000000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
00600000-00601000 r--p 00000000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
00601000-00602000 rw-p 00001000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
7f2e96e8a000-7f2e9703f000 r-xp 00000000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e9703f000-7f2e9723f000 ---p 001b5000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e9723f000-7f2e97243000 r--p 001b5000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e97243000-7f2e97245000 rw-p 001b9000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e97245000-7f2e9724a000 rw-p 00000000 00:00 0
7f2e9724a000-7f2e9726c000 r-xp 00000000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7f2e97443000-7f2e97446000 rw-p 00000000 00:00 0
7f2e97469000-7f2e9746c000 rw-p 00000000 00:00 0
7f2e9746c000-7f2e9746d000 r--p 00022000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7f2e9746d000-7f2e9746f000 rw-p 00023000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7fffa6d43000-7fffa6d64000 rw-p 00000000 00:00 0 [stack]
7fffa6dff000-7fffa6e00000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
我们按 Enter 并继续进行 2nd getchar()
。
我0x7f2e5b4dd010
以 的值进入我的终端,p
它属于7f2e5b4dd000-7f2e96e8a000
下面列表中的第一个匿名映射区域,但它不存在于上面的列表中。
$ cat /proc/`pidof mappingTest`/maps
00400000-00401000 r-xp 00000000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
00600000-00601000 r--p 00000000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
00601000-00602000 rw-p 00001000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
7f2e5b4dd000-7f2e96e8a000 rw-p 00000000 00:00 0
7f2e96e8a000-7f2e9703f000 r-xp 00000000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e9703f000-7f2e9723f000 ---p 001b5000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e9723f000-7f2e97243000 r--p 001b5000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e97243000-7f2e97245000 rw-p 001b9000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e97245000-7f2e9724a000 rw-p 00000000 00:00 0
7f2e9724a000-7f2e9726c000 r-xp 00000000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7f2e97443000-7f2e97446000 rw-p 00000000 00:00 0
7f2e97468000-7f2e9746c000 rw-p 00000000 00:00 0
7f2e9746c000-7f2e9746d000 r--p 00022000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7f2e9746d000-7f2e9746f000 rw-p 00023000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7fffa6d43000-7fffa6d64000 rw-p 00000000 00:00 0 [stack]
7fffa6dff000-7fffa6e00000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
我们按 Enter 并继续进行 3rd getchar()
。
在下面的清单中,我们看到匿名映射区域由于free()
.
$ cat /proc/`pidof mappingTest`/maps
00400000-00401000 r-xp 00000000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
00600000-00601000 r--p 00000000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
00601000-00602000 rw-p 00001000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
7f2e96e8a000-7f2e9703f000 r-xp 00000000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e9703f000-7f2e9723f000 ---p 001b5000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e9723f000-7f2e97243000 r--p 001b5000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e97243000-7f2e97245000 rw-p 001b9000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e97245000-7f2e9724a000 rw-p 00000000 00:00 0
7f2e9724a000-7f2e9726c000 r-xp 00000000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7f2e97443000-7f2e97446000 rw-p 00000000 00:00 0
7f2e97468000-7f2e9746c000 rw-p 00000000 00:00 0
7f2e9746c000-7f2e9746d000 r--p 00022000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7f2e9746d000-7f2e9746f000 rw-p 00023000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7fffa6d43000-7fffa6d64000 rw-p 00000000 00:00 0 [stack]
7fffa6dff000-7fffa6e00000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
我们按 Enter 并getchar()
在foo()
.
我在我的终端上得到0x7fffa6d60ba0
和作为第一个和第 1001 个递归调用的地址。它们现在都属于该区域(; 0x7e2000 == 8 MB)。但请注意,它以前是; 0x21000 == 132 KB。0x7fffa6582ff0
buf
foo()
[stack]
7fffa6582000-7fffa6d64000
7fffa6d43000-7fffa6d64000
$ cat /proc/`pidof mappingTest`/maps
00400000-00401000 r-xp 00000000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
00600000-00601000 r--p 00000000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
00601000-00602000 rw-p 00001000 08:05 21687266 /home/nodakai/prog/exp/mappingTest
7f2e96e8a000-7f2e9703f000 r-xp 00000000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e9703f000-7f2e9723f000 ---p 001b5000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e9723f000-7f2e97243000 r--p 001b5000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e97243000-7f2e97245000 rw-p 001b9000 08:05 14314431 /lib/x86_64-linux-gnu/libc-2.15.so
7f2e97245000-7f2e9724a000 rw-p 00000000 00:00 0
7f2e9724a000-7f2e9726c000 r-xp 00000000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7f2e97443000-7f2e97446000 rw-p 00000000 00:00 0
7f2e97468000-7f2e9746c000 rw-p 00000000 00:00 0
7f2e9746c000-7f2e9746d000 r--p 00022000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7f2e9746d000-7f2e9746f000 rw-p 00023000 08:05 14314443 /lib/x86_64-linux-gnu/ld-2.15.so
7fffa6582000-7fffa6d64000 rw-p 00000000 00:00 0 [stack]
7fffa6dff000-7fffa6e00000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
像这样使用内联汇编可能会更有趣:
#define PEEK_ESP(reg) \
__asm__ __volatile__ ( \
"movq %%rsp, %0" \
: "=r"(reg) \
)
...
void *p;
PEEK_ESP(p);