有时用一个使用大量静态内存的小 C 程序来模拟一些东西很方便。我注意到在更改为 Fedora 15 后,该程序花了 long 时间 编译。我们说的是 30 秒和 0.1 秒。更奇怪的是 ld (the 链接器)正在耗尽 CPU 并慢慢开始吃掉所有可用的 内存。经过一番摆弄后,我设法 找到这个新问题和我的交换大小之间的相关性 文件。下面是一个用于讨论目的的示例程序:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define M 1000000
#define GIANT_SIZE (200*M)
size_t g_arr[GIANT_SIZE];
int main( int argc, char **argv){
int i;
for(i = 0; i<10; i++){
printf("This should be zero: %d\n",g_arr[i]);
}
exit(1);
}
这个程序有一个巨大的数组,它的声明大小约为 200*8MB = 1.6GB 静态内存。编译这个程序需要一个 过多的时间:
[me@bleh]$ time gcc HugeTest.c
real 0m12.954s
user 0m6.995s
sys 0m3.890s
[me@bleh]$
13s 对于 ~13 行 C 程序!?那是不对的。关键号码是 静态内存空间的大小。只要它大于 总交换空间,它再次开始快速编译。例如,我 有 5.3GB 的交换空间,所以将 GIANT_SIZE 更改为 (1000*M) 以下时间:
[me@bleh]$ time gcc HugeTest.c
real 0m0.087s
user 0m0.026s
sys 0m0.027s
啊,这更像!为了进一步说服自己(和你自己,如果 你在家里尝试这个)交换空间确实是魔法 号,我尝试将可用交换空间更改为真正巨大的 19GB 并尝试再次编译 (1000*M) 版本:
[me@bleh]$ ls -ali /extraswap
5986 -rw-r--r-- 1 root root 14680064000 Jul 26 15:01 /extraswap
[me@bleh]$ sudo swapon /extraswap
[me@bleh]$ time gcc HugeTest.c
real 4m28.089s
user 0m0.016s
sys 0m0.010s
4.5 分钟后甚至没有完成!
显然链接器在这里做错了,但我不知道怎么做 解决这个问题,而不是重写程序或搞乱 带交换空间。我很想知道是否有解决方案,或者我是否有 偶然发现了一些神秘的错误。
顺便说一下,程序都编译运行正常,独立于所有的swap业务。
作为引用,这里有一些可能相关的信息:
[]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 27027
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
[]$ uname -r
2.6.40.6-0.fc15.x86_64
[]$ ld --version
GNU ld version 2.21.51.0.6-6.fc15 20110118
Copyright 2011 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
[]$ gcc --version
gcc (GCC) 4.6.1 20110908 (Red Hat 4.6.1-9)
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
[]$ cat /proc/meminfo
MemTotal: 3478272 kB
MemFree: 1749388 kB
Buffers: 16680 kB
Cached: 212028 kB
SwapCached: 368056 kB
Active: 489688 kB
Inactive: 942820 kB
Active(anon): 401340 kB
Inactive(anon): 803436 kB
Active(file): 88348 kB
Inactive(file): 139384 kB
Unevictable: 32 kB
Mlocked: 32 kB
SwapTotal: 19906552 kB
SwapFree: 17505120 kB
Dirty: 172 kB
Writeback: 0 kB
AnonPages: 914972 kB
Mapped: 60916 kB
Shmem: 1008 kB
Slab: 55248 kB
SReclaimable: 26720 kB
SUnreclaim: 28528 kB
KernelStack: 3608 kB
PageTables: 63344 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 21645688 kB
Committed_AS: 11208980 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 139336 kB
VmallocChunk: 34359520516 kB
HardwareCorrupted: 0 kB
AnonHugePages: 151552 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 730752 kB
DirectMap2M: 2807808 kB
TL;DR:当 c 程序的(大)静态内存略小于可用交换空间时,链接器需要永远链接程序。但是,当静态空间比可用交换空间略大 时,它会非常灵活。这是怎么回事!?
最佳答案
我能够在 Ubuntu 10.10 系统上重现此问题(GNU ld (GNU Binutils for Ubuntu) 2.20.51-system.20100908
),我想我已经知道了。首先,一些方法论。
在确认我在一个小型 VM(512MB 内存,2GB 交换空间)中发生这种情况后,我决定最简单的做法是使用 strace 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 做了一些意想不到的事情......它继续使用更少的内存。
奇怪,我想我们最好看看 the ld
code然后。 Drat,它不做明确的 mmap
。这必须来自一个普通的旧 malloc
内部。我们必须使用一些调试符号来构建 ld 来跟踪它。不幸的是,当我构建 bin-utils 2.21.1 时,问题就消失了。也许它已在较新版本的 bin-utils 中得到修复?
关于c - 与交换空间相关的链接器性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8233363/