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

20

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


6
如果你的 malloc() 不是可重入的,那么编写一个带锁的包装器比自己编写整个内存系统更容易些,但不改变原意。 - Carl Norum
9
"mmapped memory既不是堆heap也不是栈stack,所以我不知道你在问什么。" - Ignacio Vazquez-Abrams
8
锁定不能使非可重入函数变为可重入函数,它只能使非线程安全函数变得线程安全。可重入是一个更强的条件。 - R.. GitHub STOP HELPING ICE
3
如果@Carl希望它能够在信号处理程序中运行,那么仅靠锁是不够的。 - DigitalRoss
2个回答

32
为什么需要可重入性?唯一需要它的时候是从信号处理程序调用函数;否则,线程安全同样好用。无论是malloc还是mmap都是线程安全的。根据POSIX标准,两者都不是异步信号安全的。实际上,mmap在信号处理程序中可能工作得很好,但从信号处理程序分配内存的整个想法是非常糟糕的。
如果你想使用mmap来分配匿名内存,可以使用MAP_ANON/MAP_ANONYMOUS
p = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

曾经这是不太可移植的,系统在支持拼写方式上有所不同,因此您应该编写预处理条件来使用任何一种可用的方式,但 POSIX 已经将这两种拼写方式作为标准采纳。

传统的、丑陋的版本来自很久以前,在 MAP_ANON 出现之前:

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

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

4
没有符合规范的方法来分配内存,但“mmap”可能会起作用。更好的做法是修复需要从信号处理程序中分配内存的设计。通常情况下,信号处理程序应该什么都不做或者只设置一个标志变量或向管道中写入一个字节。 - R.. GitHub STOP HELPING ICE
3
顺便提一下,由于“什么都不做”的意思可能不太清晰,一个“什么都不做”的信号处理程序在省略SA_RESTART标记的情况下中断系统调用很有用。将一个“什么都不做”的信号处理程序设置为中断系统调用,并使用pthread_kill向特定线程发送信号是一种“自行制定”线程取消的方法,避免了pthread_cancel带来的无法修复的资源泄漏问题。如果您设置一个计时器/闹钟以生成信号,则即使只有单个线程也可以很有用,以为系统调用设置超时时间。 - R.. GitHub STOP HELPING ICE
5
最便携的版本可能是不打开/dev/zero,而是使用shm_open,它在内部执行大致相同的操作,但不需要您的文件系统拥有特殊文件。 - Jens Gustedt
3
不是这样的,你必须将映射的长度传递给munmap函数,不能只传递0。 - R.. GitHub STOP HELPING ICE
4
只需在mmap分配的块的开头存储大小,并返回指向存储大小后面一个字节的指针。然后释放该块就像备份以读取大小并将新指针和大小传递给munmap一样简单。 - R.. GitHub STOP HELPING ICE
显示剩余11条评论

10

制作一个简单的slab分配器


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

不,你不能直接使用malloc()。如果你希望它在堆上,则mmap也无法使用。

我的建议是,你应该基于malloc开发一个专门用途的slab分配器

确定你想要的对象大小,并预先分配一些数量。最初使用malloc()进行分配并保存以供后续并发使用。可以使用内置的可重入队列函数来获取和释放这些块。如果它们只需要从信号处理程序中进行管理,那么甚至这也是不必要的。

问题解决了!


1. 如果您没有这样做,那么似乎您有一个嵌入式系统或者只需使用malloc()。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接