2015-05-30 30 views
9

的C代码:的malloc() - 是否使用BRK()或mmap()的

// 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:该方法是否在堆中分配内存时开始以备将来使用?或者编译器改变分配的时间点?否则,为什么?


[更新]摘要:BRK()或mmap()的

审查TLPI和检查手册页(从TLPI的作者的帮助),现在我明白了如何malloc()决定使用brk()后或mmap(),如下:

mallopt()可以设置参数来控制的malloc()行为,有一个名为M_MMAP_THRESHOLD参数,一般来说:

  • 如果请求的内存小于它,将使用brk();
  • 如果请求的内存大于或等于它,将使用mmap();

参数的默认值是128kb(我的系统上),但在我的测试程序中,我使用1MB,所以mmap()选择,当我改变请求的内存为32KB,只见brk()将被使用。

该书提到TLPI第147页和第1035页,但我没有仔细阅读该部分。

该参数的详细信息可在手册页mallopt()中找到。

+3

'#include '? – JS1

+0

@ JS1是的,解决了这个问题,你可以给我一个解释,我是linux编程的新手...... –

+2

你需要'''sbrk()'的原型''在'unistd.h'中。如果没有原型,编译器会假定未知函数返回“int”。 – JS1

回答

11

如果我们改变了计划,看看那里的malloc“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执行分配。

+0

我发现有一个'THRESHOLD'来控制是否使用'brk()'或'mmap()',我更新了这个问题。 –

2

malloc不限于使用sbrk分配内存。例如,它可能使用mmap来映射一个大的MAP_ANONYMOUS内存块;通常mmap将远离数据段分配虚拟地址。

还有其他的可能性。特别是,mmap作为标准库的核心部分,本身并不局限于标准库函数;它可以使用操作系统特定的接口。

1

格式“%P”需要类型“无效*”的说法,但参数2的类型为“诠释”

回答问题1:编译器告诉你的参数应该是一个void * ,但是您改为提供int。如果你花五秒钟阅读和理解错误,这应该是显而易见的。有没有你不明白的部分?如果是这样,请有关更详细的问题,其迷惑你,而不是“这是为什么?” ......

了类似的警告应当对printf("%x\n", sbrk(0));,如根据the manual%x预计对应的unsigned参数。另外,根据手册:

如果任何参数不是相应转换规范的正确类型,则行为是未定义的。

通常,我们应该努力编写在任何系统上以相同方式工作的程序。为了做到这一点,我们需要制定一套规则。因此,提供了一个警告,告诉您您违反了规则并调用了未定义的行为。尽管你的未定义的行为,你的代码可能会工作,因为你期望它在你的系统上,在这个时间点 ...但是,这不应该依赖,因为在未来的某个时候你的电脑可能会获取更新,导致您的代码以微妙但破坏性的方式破解,或者可能无法在其他计算机上运行......或者可能仅选择月的时间为


问题的答案2,3和4:

该方法是否在堆中分配内存时开始以备将来使用?

不要求一个“堆”标准C的规则中存在,所以这是一个“不” ......至少,直到你告诉我们你正在使用的编译器/标准库。

或者编译器改变分配的时间点?

可能。编译器可以执行优化,甚至可以消除您的分配,前提是他们可以推断出这样做是安全的(例如可观察行为未更改)。

否则,为什么?

好问题。

为什么我们有免费考虑某些事情的规则未定义的行为并允许它运行,即使在程序员的危险之中?优化。

为什么它很重要,你分配的内存是否那张或以其他方式?你为什么要关心?只要你的记忆被分配了,对吗?只要你可以使用它,而且速度相当快。优化。

为什么编译器会执行优化?我会留下那一个让你回答;)