如何使用 mmap 在堆中分配内存?

Posted

技术标签:

【中文标题】如何使用 mmap 在堆中分配内存?【英文标题】:How to use mmap to allocate a memory in heap? 【发布时间】:2011-06-14 07:57:47 【问题描述】:

刚才提到的问题,我如何使用mmap() 在堆中分配内存?这是我唯一的选择,因为malloc() 不是可重入函数。

【问题讨论】:

如果您的malloc() 不是可重入的,那么编写一个带锁的包装器而不是滚动您自己的整个内存系统不是更容易吗? 映射的内存既不是堆也不是栈,所以我不知道你在这里问什么。 锁定不能使不可重入函数可重入。它只能使非线程安全的函数成为线程安全的。可重入是一个强得多的条件。 @Carl,如果他想让它在信号处理程序中运行,锁是不够好的。 【参考方案1】:

为什么需要重入?唯一需要的是从信号处理程序调用函数;否则,线程安全性同样好。 mallocmmap 都是线程安全的。每个 POSIX 都不是异步信号安全的。在实践中,mmap 在信号处理程序中可能工作得很好,但从信号处理程序分配内存的整个想法是一个非常糟糕的想法。

如果你想使用mmap 来分配匿名内存,你可以使用(不是 100% 可移植,但绝对是最好的):

p = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

便携但丑陋的版本是:

int fd = open("/dev/zero", O_RDWR);
p = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
close(fd);

注意MAP_FAILED,而不是NULL,是失败的代码。

【讨论】:

那么没有一致的方法来分配内存,但mmap 将“可能”工作。修复需要从信号处理程序分配内存的设计会好得多。通常,信号处理程序要么什么都不做,要么只设置一个标志变量或将一个字节写入管道。 顺便说一句,由于“什么都不做”可能不清楚,所以“什么都不做”信号处理程序在省略SA_RESTART 标志以中断系统调用时很有用。设置一个不做任何事情的信号处理程序来中断系统调用并使用pthread_kill 将信号发送到特定线程是一种“滚动你自己的”线程取消的方法,而不会出现pthread_cancel 导致的不可修复的资源泄漏问题。如果您设置计时器/警报来生成信号,为系统调用设置超时,它也可以仅用于单个线程。 最便携的版本可能不是打开/dev/zero,而是使用shm_open,它在内部做同样的事情,但不需要你的文件系统有特殊文件。 MAP_PRIVATE 对通过shm_open 获得的共享内存有效吗?我想是的,因为我找不到任何明确禁止的地方,但这似乎违反直觉。 只需将大小存储在mmap-allocated 块的开头,并返回一个指向存储大小后字节的指针。然后释放块就像备份读取大小并将新指针和大小传递给munmap一样简单。【参考方案2】:

做一个简单的slab allocator


虽然在信号处理程序中分配内存1 似乎是最好避免的事情,但它确实可以做到。

不,您不能直接使用 malloc()。如果你希望它在堆中,那么 mmap 也不起作用。

我的建议是你基于malloc做一个专用的slab allocator。

准确确定您想要的对象大小并预先分配一些对象。最初使用 malloc() 分配它们并保存它们以供以后并发使用。您可以使用本质上可重入队列和非队列函数来获取和释放这些块。如果它们只需要从信号处理程序中进行管理,那么即使这样也没有必要。

问题解决了!


1.如果你不这样做,那么看起来你有一个嵌入式系统或者可以只使用 malloc()。

【讨论】:

非常感谢。这是非常有趣的细节。

以上是关于如何使用 mmap 在堆中分配内存?的主要内容,如果未能解决你的问题,请参考以下文章

类中声明的变量的内存分配

为啥 C 中没有“memsize”,它返回使用 malloc 在堆中分配的内存块的大小?

栈与堆

Java 中的堆和栈

java堆栈的理解

JAVA虚拟机内存分配与回收机制