Я могу воспроизвести это в системе Ubuntu 10.10 (GNU ld (GNU Binutils for Ubuntu) 2.20.51-system.20100908
), и я думаю, что у меня есть ваш ответ. Сначала немного методологии.
После подтверждения того, что это происходит со мной на маленькой виртуальной машине (512 МБ оперативной памяти, 2 ГБ подкачки), я решил, что проще всего было бы связать gcc и посмотреть, что именно происходит, когда все идет к черту:
~# strace -f gcc swap.c
Подсветилось следующее:
vfork() = 3589
[pid 3589] execve("/usr/lib/gcc/x86_64-linux-gnu/4.4.5/collect2", ["/usr/lib/gcc/x86_64-linux-gnu/4."..., "--build-id", "--eh-frame-hdr", "-m", "elf_x86_64", "--hash-style=gnu", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-o", "swap", "-z", "relro", "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., ...], [/* 26 vars */]) = 0
...
[pid 3589] vfork() = 3590
...
[pid 3590] execve("/usr/bin/ld", ["/usr/bin/ld", "--build-id", "--eh-frame-hdr", "-m", "elf_x86_64", "--hash-style=gnu", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-o", "swap", "-z", "relro", "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., ...], [/* 27 vars */]) = 0
...
[pid 3590] lseek(13, 4096, SEEK_SET) = 4096
[pid 3590] read(13, ".\4@\0\0\0\0\0>\4@\0\0\0\0\0N\4@\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
[pid 3590] mmap(NULL, 1600004096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1771931000
<system comes to screeching halt>
Может показаться, что, как мы могли бы предположить, похоже, что ld
на самом деле пытается анонимно mmap
все пространство статической памяти этого массива (или, возможно, всей программы, трудно сказать, так как остальные программы настолько малы, что все может уместиться в эти дополнительные 4096).
Так что это все хорошо, но почему это работает, когда мы превышаем доступный своп в системе? Давайте повернем swapoff
и снова запустим strace -f
...
[pid 3618] lseek(13, 4096, SEEK_SET) = 4096
[pid 3618] read(13, ".\4@\0\0\0\0\0>\4@\0\0\0\0\0N\4@\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
[pid 3618] mmap(NULL, 1600004096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
[pid 3618] brk(0x60638000) = 0x1046000
[pid 3618] mmap(NULL, 1600135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
[pid 3618] mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7fd011864000
...
Неудивительно, что ld, похоже, делает то же самое, что и в прошлый раз, чтобы отобразить все пространство. но система больше не в состоянии это сделать, она терпит неудачу! ld пытается снова, и снова не удается, затем ld делает что-то неожиданное ... он перемещается с меньшим объемом памяти.
Странно, я думаю, нам лучше взглянуть на код ld
. Драт, это не делает явный mmap
. Это должно быть изнутри простого старого malloc
. Мы должны собрать ld с некоторыми символами отладки, чтобы отследить это. К сожалению, когда я собрал bin-utils 2.21.1, проблема ушла. Возможно, это было исправлено в новых версиях bin-utils?