c - 与交换空间相关的链接器性能?

标签 c linux gcc swap ld

有时用一个使用大量静态内存的小 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/

相关文章:

c - Windows 上有nftw(文件树遍历)功能吗

c++ - 通用守护进程/服务器设计 - 最佳实践(C/C++、Linux)

linux - 如何获取已安装应用程序的完整 pkg 列表(debian)?

c - 打开使用 C 编写的文件时权限被拒绝

c++ - 如何将 SFML 游戏服务器部署到 Linux 服务器?

c++ - gcc 4.8链接器链接boost线程库时出错

c++ - 内联函数有一个非内联拷贝

c - Ubuntu 上 C 语言的 sqrt

c - 字符串递归

c - 从数组中查找最长的算术级数