内存分配如何在极端情况下工作?

我很困惑如何内存分配(malloc / calloc)在linux / c工程。 假设,我有一台16GB RAM的机器,并以root身份运行一个程序。 这是64位的机器,所以所有的16GB可以作为一个单一的网段。 我可以用一个malloc调用来分配所有这些(less于OS的数量)吗? 有很多malloc调用?

这与“堆内存”和“虚拟内存”有什么关系? 如果我分配一个小内存块,它发生在堆内存,然后我调整(放大)这个块,当我接近堆栈区时会发生什么?

如果我想(几乎)所有的内存分配给我的单个进程,甚至认为它是以root身份运行的,我是否必须摆弄selimlimit RLIMIT_AS?

一个64位的进程可以分配所有的内存。 它甚至不需要是root,除非系统为非root用户定义了一个ulimit设置。 尝试ulimit -v来查看是否设置了限制。

在Linux的默认设置下,一个进程可以要求几乎任意数量的内存,它将被授予。 内存将按照使用情况进行实际分配,并根据需要从物理内存或磁盘交换中进行分配。

内存分配调整大小通常在C库中完成,方法是分配新的更大的大小,并将旧数据复制到新分配中。 这通常不是通过扩大现有分配来完成的。 内存分配选择为不与其他分配(如程序堆栈)冲突。

在虚拟内存操作系统(如Linux)上malloc()不分配内存 。 它分配地址空间例如,编译并运行下面的片段,并且(在另一个终端中)运行top

 #include <stdlib.h> #include <unistd.h> int main(void) { char *cp; cp = malloc( 16ULL * 1024 *1024 *1024); if (cp) pause(); return 0; } 

在我的电脑上,TOP显示:

 PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 29026 plasser 20 0 16.0g 324 248 S 0 0.0 0:00.00 a.out 

这意味着:a.out具有16GB的虚拟大小,并且只使用324(bytes?KB?)常驻内存(可能只是二进制文件)

发生什么事了?

  • malloc()调用已经要求操作系统扩展地址空间 ,增加16 GB
  • 操作系统已经完成了这个请求,并为它建立了页面表等等。
  • 这些页表没有附加物理内存(还),也许除了表本身
  • 一旦程序开始引用这个地址空间,页面将被附加(操作系统将会把它们错误地加入
  • (这些页面可能会从/ dev / zero中删除,但这只是一个细节)
  • 但是,在程序确实引用地址的时候,内存将不得不由OS分配并且被附加到进程中。 (它会出现在RES领域,相信我)
  • 一旦操作系统认为长时间没有使用, 连接的内存也可能被分离。 它将被添加到一个可用内存池或/和用于其他目的。 在这种情况下,它的内容可能被推送到后台存储 (磁盘)

`

malloc()可以通过扩展堆或通过mmap()来分配足够大的内存块。 他们使用哪个取决于您请求的大小和几个mallopt()选项。

如果它使用通常位于虚拟内存区域起始处的“真正的”堆部分,则它可以长到它碰到下一个分配的内存块的位置,这个内存块离开始处很远。

如果它使用mmap() ,则取决于是否有可用的连续地址空间。

/proc/<PID>/maps是查找进程的地址空间情况的一个很好的查询点。

我刚刚用Python和ctypes测试了我的64位服务器:

 import ctypes import ctypes.util m=ctypes.CDLL(ctypes.util.find_library("c")).malloc m.argtypes=(ctypes.c_uint64,) m.restype = ctypes.c_uint64 

现在我有了m ,它可以调用libc的malloc()函数。

现在我可以做

 >>> hex(m(2700000000)) '0x7f1345e3b010L' >>> hex(m(2700000000)) '0x7f12a4f4f010L' >>> hex(m(2700000000)) '0x7f1204063010L' >>> hex(m(2700000000)) '0x7f1163177010L' >>> hex(m(2700000000)) '0x7f10c228b010L' >>> hex(m(2700000000)) '0x7f102139f010L' >>> hex(m(2700000000)) '0x7f0f804b3010L' 

但由于任何原因,我不能做

 >>> hex(m(2900000000)) '0x0L' 

因为它返回0和 一个NULL指针。

现在做

 print open("/proc/self/maps").read() 

显示所述文件的行; 某处

 7f0ed8000000-7f0ed8021000 rw-p 00000000 00:00 0 7f0ed8021000-7f0edc000000 ---p 00000000 00:00 0 7f0edf5c7000-7f13e6d27000 rw-p 00000000 00:00 0 

我得到了我的分配。

但是,如果我这样做

 >>> hex(m(1)) '0x1f07320L' >>> hex(m(1)) '0x1f0c0e0L' >>> hex(m(1)) '0x1f02d50L' >>> hex(m(1)) '0x1f02d70L' >>> hex(m(1)) '0x1ef94f0L' >>> hex(m(1)) '0x1eb7b20L' >>> hex(m(1)) '0x1efb700L' >>> hex(m(270)) '0x1f162a0L' >>> hex(m(270)) '0x1f163c0L' >>> hex(m(270)) '0x1f164e0L' >>> hex(m(270)) '0x1f16600L' 

内存地址来自于

 >>> print open("/proc/self/maps").read() [...] 01d2e000-01f2b000 rw-p 00000000 00:00 0 [heap] [...] 

指定的堆。

请注意,虽然我可以得到这个内存分配,使用它都造成伤害,因为我的过程可能会被着名的OOM杀手杀害。