c - 在 32 位 Linux 中无法耗尽物理内存
问题描述
所以我有一个有趣的基于操作系统的问题给你。在过去的几个小时里,我一直在与我认识的任何有 C 编程经验的人交谈,但似乎没有人能够就为什么会发生这种行为给出明确的答案。
我有一个故意设计为导致极端内存泄漏的程序(例如,当您在分配内存后不释放内存时会发生什么)。在 64 位操作系统(Windows、Linux 等)上,它会做它应该做的事情。它填充物理内存,然后填充操作系统的交换空间。在 Linux 中,该进程随后被操作系统终止。然而,在 Windows 中,它不是,它会继续运行。最终结果是系统崩溃。
这是代码:
#include <stdlib.h>
#include <stdio.h>
void main()
{
while(1)
{
int *a;
a = (int*)calloc(65536, 4);
}
}
但是,如果您在 32 位 Linux 发行版上编译并运行此代码,则它对物理内存的使用完全没有影响。它使用了我分配的 4 GB RAM 的大约 1%,之后就再也没有增加。我没有要测试的 32 位 Windows 的合法副本,所以我不能确定这也发生在 32 位 Windows 上。
有人可以解释为什么使用 calloc 会填充 64 位 Linux 操作系统的物理内存,而不是 32 位 Linux 操作系统吗?
解决方案
malloc
and函数在calloc
技术上并不分配内存,尽管它们的名字。它们实际上为您的程序地址空间的一部分分配了操作系统级别的读/写权限。这是一个微妙的差异,大多数时候并不相关。
正如所写的那样,这个程序只消耗地址空间。最终,calloc
将开始返回 NULL 但程序将继续运行。
#include <stdlib.h>
// Note main should be int.
int main() {
while (1) {
// Note calloc should not be cast.
int *a = calloc(65536, sizeof(int));
}
}
如果您写入从 calloc 返回的地址,它将强制内核分配内存来支持这些地址。
#include <stdlib.h>
#include <string.h>
int main() {
size_t size = 65536 * 4;
while (1) {
// Allocates address space.
void *p = calloc(size, 1);
// Forces the address space to have allocated memory behind it.
memset(p, 0, size);
}
}
仅写入返回的块中的单个位置是不够的,calloc
因为分配实际内存的粒度是 4 KiB(页面大小...... 4 KiB 是最常见的)。因此,您只需写入每一页即可。
64位的情况呢?
分配地址空间有一些记账开销。在 64 位系统上,您会获得 40 或 48 位的地址空间,其中大约一半可以分配给程序,即至少 8 TiB。在 32 位系统上,这达到 2 GiB 左右(取决于内核配置)。
因此,在 64 位系统上,您可以分配 ~8 TiB,而在 32 位系统上,您可以分配 ~2 GiB,而开销是导致问题的原因。malloc
对或的每次调用通常都会产生少量开销calloc
。
推荐阅读
- php - 使用 PHP 下载和存储远程密码保护文件
- r - 如何根据另一列中的值填充列?
- regex - 如果列名是动态的,如何将列名重命名为数据框中的新值
- firefox - 如何制作:newtab(新标签页)在暗模式下,而暗模式在 Firefox 69+ 中激活
- scala - 如何使用 Spark 和 Whislabs/docker-it-scala 解决 SBT 依赖问题
- powershell - 获取路径上图像文件的创建日期
- django - 最后带有点的请求会给出错误的请求 400
- php - 制作 CMS 用户个人资料页面
- java - 线程销毁后,锁如何保留在线程持有的对象上?
- sbt - 获取系统属性在调试日志级别无法正常工作