【问题标题】:How to turn off mmap usage for malloc() in multithread?如何在多线程中关闭 malloc() 的 mmap 使用?
【发布时间】:2017-05-01 21:15:09
【问题描述】:

似乎malloc() 更喜欢使用mmap() 在多线程程序中分配空间。我刚刚尝试设置M_TRIM_THRESHOLDM_MMAP_MAX 来关闭mmap 的使用但失败了:

// Turn off malloc trimming.
mallopt(M_TRIM_THRESHOLD, -1);

// Turn off mmap usage.
mallopt(M_MMAP_MAX, 0);

一段简单的测试代码如下:

#include <malloc.h>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

void alloc_assert()
{
    // Turn off malloc trimming.
    mallopt(M_TRIM_THRESHOLD, -1);

    // Turn off mmap usage.
    mallopt(M_MMAP_MAX, 0);

    void* p = malloc(100);

    printf("size_t(p): %zu\n", size_t(p));

    assert(size_t(p) < 0x100000000000l);
}

void* thread_func(void *arg)
{
    alloc_assert();

    pthread_exit(NULL);

    return NULL;
}

int main()
{
    pthread_t thr[2];
    int data = 0;

    // Multi-thread enabled.
    if (pthread_create(&thr[0], NULL, &thread_func, (void*) &data) != 0)
    {
        printf("Create thread error\n");
    }

    pthread_join(thr[0], NULL);

    //alloc_assert();

    return 0;
}

输出如下:

size_t(p): 140154111002816
a.out: main.cpp:37: void alloc_assert(): Assertion `size_t(p) < 0x100000000000l' failed.
[1]    154060 abort      ./a.out

malloc() 在高地址而不是普通堆地址上分配了空间。但是,如果我们将main() 中的代码更改为以下代码:

int main()
{
    alloc_assert();

    return 0;
}

输出是:

size_t(p): 31775776

malloc() 不是使用mmap(),而是在普通堆上分配空间。我想是否可以在多线程程序中关闭mmap()malloc() 的使用?

我的环境配置:

Thread model: posix
gcc version 5.2.0 (GCC)
Linux fsdev32 2.6.32-573.el6.x86_64

【问题讨论】:

  • 为什么要禁用mmap()ed 堆空间?
  • @EOF 我们保留了大于 0x100000000000l 的地址来存储我们的系统数据。 malloc() 通过 mmap() 将破坏我们的数据,因为它也会占用这些地址。
  • @JimMa:如果您需要自己的特殊地址范围,请先映射它们。
  • 不使用 mmap 就意味着只有 sbrk?
  • @Adalcar 是的。至少 malloc() 在多线程代码块中。

标签: c++ c multithreading malloc mmap


【解决方案1】:

总结

看起来原因是 glibc 的 malloc 使用了多个 malloc “arenas”,并为每个新线程提供了自己的 arena,无论您将 M_MMAP_MAX 设置为什么。

如果该领域有空间,glibc 会将其提供给您的 malloc() 请求。

您可以使用 mallopt(M_ARENA_MAX, 1) 禁用它。

详情

我通过导入#include &lt;malloc.h&gt;(特定于glibc)并更改您的代码以显示malloc_stats() 统计信息来确认这一点:

malloc_stats();
puts("");
void* p = malloc(100);
malloc_stats();

打印出来:

Arena 0:
system bytes     =     135168
in use bytes     =       1328
Total (incl. mmap):
system bytes     =     135168
in use bytes     =       1328
max mmap regions =          0
max mmap bytes   =          0

Arena 0:
system bytes     =     135168
in use bytes     =       1328
Arena 1:
system bytes     =     135168
in use bytes     =       2336
Total (incl. mmap):
system bytes     =     270336
in use bytes     =       3664
max mmap regions =          0
max mmap bytes   =          0

我们可以看到一个新的Arena 1 是如何添加135168 很多字节的。

strace -fy -e mmap ./yourprogram,我们可以看到这个竞技场的创建:

[pid  8704] mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7efefe1a7000
[pid  8704] munmap(0x7efefe1a7000, 31821824) = 0
[pid  8704] munmap(0x7eff04000000, 35287040) = 0
[pid  8704] mprotect(0x7eff00000000, 135168, PROT_READ|PROT_WRITE) = 0

size_t(p): 139633681762496; address: 0x7eff000008c0

所以看起来 glibc mmaps 为新竞技场提供了一些内存,释放了其中的一部分,然后 mprotect()s 135168 很多字节——就像在 malloc_stats() 中显示的竞技场一样多.

这向我暗示man mallopt

Setting this parameter to 0 disables the use of mmap(2)
for servicing large allocation requests.

不完全正确:当它想创建一个新的竞技场时,它仍然会mmap

您显然可以使用以下方法避免这种情况:

mallopt(M_ARENA_MAX, 1);

【讨论】:

    猜你喜欢
    • 2012-03-27
    • 2020-09-12
    • 1970-01-01
    • 1970-01-01
    • 2015-08-13
    • 1970-01-01
    • 2021-11-27
    • 1970-01-01
    • 2014-07-20
    相关资源
    最近更新 更多