我很担心内存分配(malloc / calloc)在linux / c中是如何工作的。假设我有一台16GB RAM的机器,我以root身份运行程序。它是64位机器,因此所有16GB都可以作为单个段进行寻址。我可以使用单个malloc调用分配所有这些(当然,减去操作系统的数量)吗?有很多malloc电话?
它是如何与“堆内存”和“虚拟内存”相关的?如果我分配一个小的内存块,它发生在堆内存中,那么我调整(放大)这个块,当我接近堆栈区域时会发生什么?
如果我想(几乎)将所有RAM分配给我的单个进程,我是否必须摆弄setrlimit RLIMIT_AS,即使它认为它以root身份运行?
答案 0 :(得分:5)
在虚拟内存操作系统(如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(字节?KB?)驻留内存(可能只是二进制文件)
发生了什么事?
RES
字段中,相信我)`
答案 1 :(得分:2)
malloc()
可以通过扩展堆或mmap()
足够大的内存块来分配内存。它使用哪个取决于您请求的大小以及几个mallopt()
选项。
如果它使用通常位于虚拟内存区域开头的“真实”堆部分,它可以长大到达下一个分配的内存块的位置,该内存块远离开始。 / p>
如果它使用mmap()
,则取决于是否有可用的连续地址空间。
/proc/<PID>/maps
是查找进程地址空间情况的一个很好的查询点。
我刚用我的64位服务器测试了Python和ctypes:
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 resp。一个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杀手杀死。
答案 2 :(得分:0)
64位进程可以分配所有内存。除非系统为非root用户定义了ulimit
设置,否则它甚至不需要是root用户。请尝试ulimit -v
查看是否设置了限制。
在Linux默认设置下,进程可以请求几乎任何数量的内存,并且它将被授予。内存将在使用时实际分配,它将根据需要来自物理RAM或磁盘交换。
内存分配调整大小通常在C库中完成,方法是分配新的更大的大小并将旧数据复制到新的分配中。通常不会通过扩展现有分配来完成。选择内存分配不与其他分配(如程序堆栈)冲突。