首页 文章

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

提问于
浏览
2

我很困惑内存分配(malloc / calloc)在linux / c中的工作原理 . 假设我有一台16GB RAM的机器,我以root身份运行程序 . 它是64位机器,因此所有16GB都可以作为单个段进行寻址 . 我可以使用单个malloc调用分配所有这些(当然,减去操作系统的数量)吗?有很多malloc电话?

它是如何与“堆内存”和“虚拟内存”相关的?如果我分配一个小内存块,并且它发生在堆内存中,那么我调整(放大)这个块,当我接近堆栈区域时会发生什么?

如果我想(几乎)将所有RAM分配给我的单个进程,甚至认为它以root身份运行,我是否必须使用setrlimit RLIMIT_AS?

3 回答

  • 0

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

    在Linux默认设置下,进程可以请求几乎任何数量的内存,并且它将被授予 . 内存将在使用时实际分配,并且它将根据需要来自物理RAM或磁盘交换 .

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

  • 2

    在虚拟内存OS(例如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?)驻留内存(可能只是二进制)

    发生了什么?

    • malloc()调用已要求操作系统扩展 address space ,增加16 GB

    • 操作系统已完成此请求,并为其设置页面表等 .

    • 这些页面表没有附加到它们的物理内存(可能除了表格本身)

    • 一旦程序开始引用此地址空间,将附加页面(操作系统将故障)

    • (页面可能是/ dev / 0的COW,但这只是一个细节)

    • 但是,在程序确实引用地址时,内存必须由OS分配并附加到进程 . (它将显示在 RES 字段中,相信我)

    • 在某个时刻,一旦操作系统认为它已经使用了很长时间,附加的内存也可以分离 . 它将被添加到可用内存池中或/和用于其他目的 . 在这种情况下,其内容可能会被推送到后备存储(磁盘)

    `

  • 5

    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杀手杀死 .

相关问题