c code:
// program break mechanism
// TLPI exercise 7-1
#include <stdio.h>
#include <stdlib.h>
void program_break_test() {
printf("%10p\n", sbrk(0));
char *bl = malloc(1024 * 1024);
printf("%x\n", sbrk(0));
free(bl);
printf("%x\n", sbrk(0));
}
int main(int argc, char **argv) {
program_break_test();
return 0;
}
编译以下代码时:
printf("%10p\n", sbrk(0));
我收到警告提示:
format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘int’
问题1:为什么会这样?
在我malloc(1024 * 1024)
之后,似乎程序中断并没有改变。
这是输出:
9b12000
9b12000
9b12000
问题2:进程在启动时是否会在堆上分配内存以供将来使用?或者编译器改变分配的时间点?否则,为什么?
[update]摘要:brk()或mmap()
在查看TLPI并检查手册页(在TLPI作者的帮助下)之后,现在我了解malloc()
如何决定使用brk()
或mmap()
,如下所示:
mallopt()
可以设置参数来控制malloc()
的行为,并且通常会有一个名为M_MMAP_THRESHOLD
的参数:
brk()
; mmap()
; 参数的默认值是128kb
(在我的系统上),但在我的测试程序中我使用1Mb,因此选择了mmap()
,当我将请求的内存更改为32kb时,我看到{ {1}}将被使用。
书中提到TLPI第147页和第1035页,但我没有仔细阅读该部分。
参数的详细信息可以在brk()
的手册页中找到。
答案 0 :(得分:12)
如果我们更改程序以查看malloc
&#39; d内存的位置:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void program_break_test() {
printf("%10p\n", sbrk(0));
char *bl = malloc(1024 * 1024);
printf("%10p\n", sbrk(0));
printf("malloc'd at: %10p\n", bl);
free(bl);
printf("%10p\n", sbrk(0));
}
int main(int argc, char **argv) {
program_break_test();
return 0;
}
sbrk
不会发生变化,这可能更清楚一些。 malloc
给我们的记忆被映射到一个截然不同的位置。
您还可以在Linux上使用strace
查看系统调用,并发现malloc
正在使用mmap
执行分配。
答案 1 :(得分:2)
malloc
不仅限于使用sbrk
来分配内存。例如,它可能使用mmap
来映射大的MAP_ANONYMOUS
内存块;通常mmap
将远离数据段分配虚拟地址。
还有其他可能性。特别是,作为标准库的核心部分的mmap
本身并不局限于标准库函数;它可以使用特定于操作系统的接口。
答案 2 :(得分:1)
格式'%p'需要'void *'类型的参数,但参数2的类型为'int'
对问题1的回答:编译器告诉你参数应该是void *
,但你提供的是int
。如果你花五秒钟阅读并理解错误,这应该是显而易见的。你有什么不明白的地方吗?如果是这样,请问一个更加详细的问题,让您感到困惑,而不是&#34;为什么会这样?&#34; ......
printf("%x\n", sbrk(0));
应发生类似警告,因为根据the manual,%x
预计会对应unsigned
参数。另外,根据手册:
如果任何参数不是相应转换规范的正确类型,则行为未定义。
通常,我们应该努力编写在任何系统上以相同方式工作的程序。为此,我们需要建立一套规则。因此,会提供警告,告诉您违反规则并调用未定义的行为。尽管您的行为未定义,但您的代码可能正如您希望的那样在您的系统上,此时 ...但是,这不应该是可以依赖,因为在未来的某个时候,您的计算机可能会获取一个更新,导致您的代码以微妙但破坏性的方式破解,或者可能无法在其他计算机上运行...或者可能只是选择那个时间。
问题2,3和4的答案:
进程在开始供将来使用时是否在堆上分配内存?
没有要求堆积&#39;存在于标准C的规则之内,因此,至少,直到你告诉我们你正在使用哪个编译器/标准库。
或者编译器改变分配的时间点?
可能。允许编译器执行甚至可能消除您的分配的优化,只要他们可以推断它是安全的(例如可观察行为不是&#39;改变了。)
否则,为什么?
好问题。
为什么我们有规则可以自由地考虑某些未定义的行为并允许它运行,即使在程序员的危险中也是如此?优化
为什么分配的内存是在堆上还是其他方面?你为什么要关心?只要你的记忆被分配,对吗?只要你能使用它,它就会相当快。优化
为什么编译器会执行优化?我会留下那个给你回答;)
答案 3 :(得分:0)
如果您在代码中使用malloc,它将在开头调用brk(),从堆中分配了0x21000字节,这是您打印的地址,因此问题1:可以满足以下mallocs要求:分配的空间,因此这些malloc实际上并未调用brk,这是malloc中的优化。如果下次您要分配超出该范围的大小,则将调用一个新的brk(如果不大于mmap阈值)。