何时使用valloc()比malloc()更合适?

16
C语言(和C++)包括一系列动态内存分配函数,其中大多数具有直观的名称,对于基本了解内存的程序员来说很容易解释。 malloc() 简单地分配内存,而 calloc() 分配一些内存并急切地清除它。 还有 realloc()free(),它们相当简单明了。 malloc() 的manpage还提到了 valloc() 函数,该函数将(`size`)字节分配给页面边界对齐。
不幸的是,我的背景在低级细节方面不够充分。 分配和使用页面边界对齐的内存有什么影响,以及何时适用于常规的 malloc()calloc()

2
简短回答:在大多数架构中,这是因为性能的原因,但您需要一个特定于实现的函数。 - Adriano Repetti
1
这可能是一个有用的阅读材料:https://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.bpxbd00/rvallo.htm - JAL
我没有看到valloc是标准C或POSIX。因此,你的问题有点无用。如果你需要特定的对齐方式,请使用aligned_alloc - too honest for this site
@Olaf 如果您能进一步阐述,我相信您的评论可以成为一个建设性的答案。 - Jules
@Jules:完成了。欢迎留言。 - too honest for this site
显示剩余2条评论
4个回答

15

valloc函数的man手册包含一条重要的注释:

函数valloc()出现在3.0BSD中。在4.3BSD中,它被记录为已过时,在SUSv2中被记录为遗留代码。它不出现在POSIX.1-2001中。

valloc已经过时且非标准化 - 回答你的问题,新编写的代码中永远不会适用它。

虽然有一些原因需要分配对齐的内存 - this question列举了一些好的原因 - 但通常最好让内存分配器决定给你哪块内存。如果您确定需要将新分配的内存对齐到某个位置,请改用aligned_alloc(C11)或posix_memalign(POSIX)。


那个问题中提到的内容并不适用于使用页面对齐进行分配。 - Nate Eldredge
glibc手册仍然奇怪地提到它,甚至给出了一种实现。 - Wander Nauta
@iharob:在最新版本的Ubuntu Xenial中,vallocposix_memalign手册页中有讨论,而man valloc将会调出它。 - Nate Eldredge
@NateEldredge OP 表示在 malloc() 手册页中提到了这一点。我现在也在 Ubuntu Xenial 上,但没有找到相关内容。但我看到了混淆之处,可能是我不小心删除了部分评论并没有注意到。 - Iharob Al Asimi

5
使用页面对齐进行分配通常不是为了提高速度,而是因为您想利用处理器的MMU的某些功能,它通常以页面粒度工作。
一个例子是如果您想使用mprotect(2)更改该内存的访问权限。例如,假设您想将一些数据存储在内存块中,然后将其设置为只读,以便尝试在那里写入的任何错误部分都会触发segfault。由于mprotect(2)只能逐页更改权限(因为这是底层CPU硬件可以强制执行的内容),因此存储数据的块最好是页面对齐的,并且其大小最好是页面大小的倍数。否则,您设置为只读的区域可能包括仍需要写入的其他不相关数据。
或者,您将在内存中生成一些可执行代码,然后稍后要执行它。默认情况下分配的内存可能没有设置允许执行代码的权限,因此您将不得不使用mprotect给予其执行权限。同样,这必须以页面粒度完成。
另一个例子是如果您现在想分配内存,但稍后可能想在其上映射一些内容。
因此,总的来说,对页面对齐内存的需求将涉及某些相当低级别的应用程序,通常涉及某些特定于系统的内容。如果您需要它,您会知道的。(如前所述,您应该使用posix_memalign或匿名mmap进行分配,而不是valloc。)

-1

显然的答案是,在应用程序(虚拟)内存使用模式下,malloc不适用(效率较低),valloc更加适合(更高效),因此在这种情况下应该使用valloc。这将取决于操作系统、库和架构以及应用程序…

传统上,malloc会从可用的空闲内存中分配真实内存,如果没有,则通过增加brk点来分配内存,在这种情况下,出于安全原因,操作系统会将其清除。

calloc在愚蠢的实现中会执行malloc,然后重新清除内存,而聪明的实现则会避免重新清除由操作系统自动清除的新分配的内存。

valloc与虚拟内存有关。在使用文件系统的虚拟内存系统中,您可以分配大量的内存或文件空间/交换空间,甚至超过物理内存,并且它将被页面交换,因此对齐是一个因素。在Unix中,创建指定文件并添加/删除页面是使用inode来定义文件完成的,但直到需要时才处理实际的磁盘块,在这种情况下,它会清除它们。因此,我希望valloc系统可以增加数据段交换的大小,而不实际分配物理或交换页面,也不运行for循环来全部清除-因为文件和分页系统会根据需要进行清除。因此,valloc应该比malloc快得多。但是,与calloc一样,如何特定的*x/C风格实现它取决于他们自己,并且valloc man页面对于这些期望毫无帮助。

传统上,这是通过brk / sbrk实现的。当然,在虚拟内存系统中,无论是分页还是分段系统,都没有必要进行任何brk / sbrk操作,只需将文件或地址空间中的最后位置写入以扩展到该点即可。

关于分配到页面边界,这通常不是用户想要或需要的,而是系统想要或需要的。

一种(可能更昂贵的)模拟valloc的方法是确定页面边界,然后使用此对齐规范调用aligned_alloc或posix_memalign。
valloc已被弃用、删除或在某些操作系统中不需要,但这并不意味着它在其他操作系统中不再有用或必要以实现最佳效率。如果已经被弃用或删除,我们希望有替代方案同样高效(但我无法保证,并且实际上可能会编写自己的malloc替代方案)。
在过去的40年里,真实内存和虚拟内存的权衡周期性地发生变化,主流操作系统往往追求华丽而非效率。对于那些没有时间或空间效率作为主要任务的程序员来说,嵌入式系统中的效率更加关键,但即使在那里,效率通常也受到标准操作系统和/或工具的支持不足。但当你存在疑虑时,你可以为你的应用程序自行编写malloc替代方案,以执行所需操作,而不依赖于其他人的决定或实现结果。
因此,真正的答案是你不一定想要使用valloc、malloc、calloc或任何你当前操作系统的替代方案。

“在这种情况下,[内存]由操作系统出于安全原因而清除”这是完全错误的。不能保证环境会以这种方式运行。此外,我认为这个答案没有为问题线程增加任何新内容。 - Jules
@Jules - 有什么问题吗?如果你(或malloc)使用brk来获取新的内存(可能已被其他用户/进程使用),操作系统会将其清除,否则就会存在安全问题。在现代操作系统中,当分配物理内存时,虚拟内存系统会处理这个问题。说valloc已经过时并不是回答这个问题的答案(它是否仍然有用取决于操作系统):根据描述条件,valloc在(虚拟内存分配)下更快,这也是使用valloc与malloc的原始问题的答案,也是我们为什么要有valloc的原因。 - David M W Powers
在现代操作系统中,你正在做出一些非常强烈的假设。有许多现代操作系统(例如在嵌入式和某些物联网应用中)不强制执行在通常桌面和服务器使用的操作系统中被视为理所当然的某些安全措施。顺便说一句,在许多已经因过时而完全删除了实现的操作系统上,valloc无法更快。 - Jules
@Jules - 问题不在于是否有没有虚拟机或不执行安全性的操作系统,或者valloc更快的情况,而是何时更适合使用valloc。如果嵌入式系统不支持VM或不执行安全性,则不太可能需要valloc。同样,如果它使用了针对VM系统页面边界进行优化并避免清除已分配但未使用内存的VM敏感版本的malloc,那么也不需要valloc。然而,传统上人们编写自己的malloc是因为它不够优秀。 - David M W Powers

-1
首先,valloc已过时,应使用memalign代替。
其次,它根本不是C(C ++)标准的一部分。
这是一种特殊的内存分配方式,被对齐到_SC_PAGESIZE边界。
什么情况下有用呢?我猜永远不需要,除非有特定的低级要求。 如果你需要它,你会知道需要它,因为它很少有用(也许只有在尝试一些微观优化或在进程之间创建共享内存时可能有用)。

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