有时,用一个使用大量静态内存的小C程序来模拟一些东西是很方便的.我注意到在换上Fedora 15后,这个程序花了long分钟才完成

#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);
}

这个程序有一个巨大的数组,声明大小约为

[me@bleh]$ time gcc HugeTest.c 

real    0m12.954s
user    0m6.995s
sys 0m3.890s

[me@bleh]$

13行C程序的13秒!?那是不对的.关键号码是

[me@bleh]$ time gcc HugeTest.c 

real    0m0.087s
user    0m0.026s
sys 0m0.027s

啊,更像是这样!进一步说服自己(和你自己,如果有的话)

[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分钟后都没有完成!

显然链接器在这里做错了什么,但我不知道怎么做

顺便说一句,这些程序都能正确编译和运行,与所有掉期业务无关.

以下是一些可能的相关信息供参考:

[]$ 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程序的(大)静态内存略小于可用的交换空间时,链接器将永远无法链接程序.然而,当静态空间比可用交换空间略大larger时,它就相当快了.怎么回事!?

推荐答案

我能够在Ubuntu 10.10系统(GNU ld (GNU Binutils for Ubuntu) 2.20.51-system.20100908)上重现这一点,我想我有你的答案.首先,一些方法.

在一个小型虚拟机(512MB内存,2GB交换)中证实了这一点后,我决定最简单的方法是扫射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似乎做了和上次一样的事情,对整个空间进行了mmap.但这个系统已经无法做到这一点,它失败了!ld再次try ,但再次失败,然后ld做了一些意想不到的事情...它以更少的内存继续前进.

奇怪,我想我们最好看看the ld code.德拉特,它没有明确的mmap.这一定是从一辆普通的旧malloc车里面来的.我们必须用一些调试符号来构建ld来追踪这一点.不幸的是,当我构建bin-utils 2.21.1时,问题就消失了.也许它已经在新版本的bin utils中修复了?

Linux相关问答推荐

将UTC字符串日期时间转换为毫秒UTC时间戳

Linux/gcc中的文件创建时间系统调用

awk:try 将时间戳字符串转换为unix纪元时间

IntelliJ(PyCharm)不再识别Linux中的AltGr快捷键

在 bash 中使用 tee 时如何返回错误代码

Linux 'column' 实用程序的使用

如何在充满 csv 的目录中获得不同的值计数

如何在 Linux 上打印带有屏蔽值的环境名称?

使用 awk 将 csv 拆分为带有标题的多个文件

函数在 shell 脚本中抛出错误语法错误:} unexpected

如何指定链接时使用的库版本?

如何在 shell 脚本中动态生成新的变量名?

使用 rc.local 运行脚本:脚本有效,但在启动时无效

Vagrant chicken-and-egg:与 uid = apache 用户共享文件夹

任何方式以编程方式在android上运行shell命令?

Supervisord - 将进程标准输出重定向到控制台

初学者如何在 Linux 中开始使用 Mono?

如何从 shell 确定当前的 CPU 利用率?

在linux上上几个目录

在tmux中绑定Ctrl+Tab和Ctrl+Shift+Tab