为什么这个非常小的C程序会占用这么多内存?

为什么这个非常小的C程序会占用这么多内存?,c,memory,C,Memory,上面的程序(使用gnu g++编译,没有额外的编译器标志)只会永远循环,似乎占用了比它应该使用的更多的内存(顶部的输出如下所示): 我明白为什么CPU使用率是100%,因为它在while循环中不断旋转。为什么VIRT的容量是4MB?为什么SHR的存储空间为728KB?我没有使用图书馆。最后,也是最重要的一点,为什么使用788KB只存储一个变量?剩余(4376-788)KB存储/使用在哪里/如何使用 为什么VIRT的容量是4MB?为什么SHR的存储空间为728kB?我没有使用图书馆 这是不准确的

上面的程序(使用gnu g++编译,没有额外的编译器标志)只会永远循环,似乎占用了比它应该使用的更多的内存(顶部的输出如下所示):

我明白为什么CPU使用率是100%,因为它在while循环中不断旋转。为什么VIRT的容量是4MB?为什么SHR的存储空间为728KB?我没有使用图书馆。最后,也是最重要的一点,为什么使用788KB只存储一个变量?剩余(4376-788)KB存储/使用在哪里/如何使用

为什么VIRT的容量是4MB?为什么SHR的存储空间为728kB?我没有使用图书馆

这是不准确的

让我们编译:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                
 9392 root      20   0    4376    788    728 R 100.0  0.0   2:32.65 a.out  
(如果在
pmap-x
输出中看到重复的行,则在旧版本中这是一个错误)

因此,它使用

  • libc.so
    -C标准库
  • ld.so
    -动态链接器
1000kB的RSS被以下用户占用:

  • 920kB是可执行文件(4kB)和共享库(带有
    r-x--
    模式的页面)的可执行代码
  • 12kB是堆栈(标记为
    [stack]
  • 标记为
    [anon]
    且带有
    rw---
    模式的页面是可执行库和共享库的
    .bss
    部分的堆和0初始化数据,具有静态存储持续时间
  • 其余为只读和非0初始化数据,静态存储持续时间来自可执行库和共享库的
    .data
    .rodata
    r--
    模式)部分

让您吃惊的大部分内存开销来自C库和动态链接器,即使您不使用它们,它们也会完全加载到内存中。。。很(您使用的是一组代码,它们运行在Mux</C>),许多人没有意识到,但它在那里。它负责建立STDIO,运行C++全局构造函数,并安排它可以从<代码>主< /代码>中返回,而不会崩溃。 相比之下,这几乎是Linux/x86上最小的忙等待程序,没有:

我用汇编语言编写了它,这样我就可以省去所有与C库相关的开销。为了排除在
main
之前运行的代码,我必须将我的程序命名为“main function”
\u start
。编辑如下:

$ cat tiny.s
    .text
    .globl _start
    .type _start,@function
_start:
    pause
    jmp _start
    .size _start, .-_start
    .section .note.GNU-stack,"",@progbits
-nostlib
关闭大部分C库,
-nostartfiles
关闭在
main
之前运行的代码,
-static
因此它甚至不会拉入动态链接器,
-Wl,--build id=none
抑制使可执行文件在磁盘上显著增大的注释。这就是我们得到的:

$ gcc -nostdlib -nostartfiles -static -Wl,--build-id=none -o tiny tiny.s
这仍然分配156kB的地址空间和4kB的实际RAM。为了更详细地了解这个空间将要做什么,我们可以查看流程的
/proc//maps
:(注意,您系统上的输出可能会略有不同)

有五种虚拟内存分配,每行的前两个数字分别是它们的起始地址和结束地址。可执行文件的第一个0x1000字节(4kB)已映射为只读,可执行文件的第二个4kB已映射为read execute。(是的,这意味着文件比它的内存映射要短。内核将用零来填补空白。)然后我们有了 0x7fff5c9d4000− 0x7fff5c9b3000=132kB分配给堆栈,12kB分配给“vvar”,4kB分配给“vdso”。8+132+12+4=156kB

这里可以看到一个有趣的事实:
top
的RES只统计已提交到当前进程的页面。在本例中,这是堆栈分配的一页。来自可执行文件的8kB映射不被计算在内,因为它们是只读的、可共享的和可丢弃的——如果有许多进程运行同一个程序,它们将在物理RAM中共享相同的程序代码副本,并且如果内核需要将这些页从RAM中踢出,以便为其他内容腾出空间,它不必将它们写入交换文件。(顶部的
手册页对RES的描述有所不同,但据我所知,这是错误的。)

“vvar”和“vdso”映射分别是由内核提供给Linux上所有用户空间进程的小数据和代码。它们用于一些低级技巧,比如在不将CPU切换到内核模式的情况下执行
gettimeofday
。这就减少了几千个周期的开销,这对精确计时很重要。据我所知,没有办法关掉这些


您可以使用
ulimit
命令减小堆栈分配的大小。例如,
ulimit-s4
将其削减到绝对最小值4kB。如果我像这样运行我的程序

$ cat /proc/12514/maps
000000400000-000000401000 r--p 00000000 fd:01 26477890    /home/zack/tiny
000000401000-000000402000 r-xp 00001000 fd:01 26477890    /home/zack/tiny
7fff5c9b3000-7fff5c9d4000 rw-p 00000000 00:00 0           [stack]
7fff5c9fb000-7fff5c9fe000 r--p 00000000 00:00 0           [vvar]
7fff5c9fe000-7fff5c9ff000 r-xp 00000000 00:00 0           [vdso]
然后
top
报告

$ (unset $(printenv | cut -d= -f1); ulimit -s 4; exec ./tiny)
/proc/14293/maps
中的
堆栈

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  14293 zack      20   0      28      4      0 R 100.0   0.0   0:06.02 tiny
但是如果没有初始的
unset
命令(清除所有环境变量),程序在启动时崩溃:

7ffdf80e5000-7ffdf80e6000 rw-p 00000000 00:00 0              [stack]
这是因为内核在启动程序运行之前,会将一组数据写入堆栈分配中——命令行参数向量、所有环境变量和。如果我不清除环境变量,数据将占用超过4kB的空间,程序将崩溃。我打赌您不知道在
execve
系统调用中可能触发segfault



1正确的二进制千字节,即:1kB≝ 1024字节。不要听任何人说不同的话,即使是国际度量衡局也不行。

这不是一个真正的C问题,更像是一个平台/操作系统问题,但我猜这里有一个4MB的堆栈。你可能得到了整个(共享)C库。“我没有使用库。”这是一种错觉。你的p
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND  
12154 zack      20   0     156      4      0 R 100.0   0.0   0:16.51 a.out
$ cat /proc/12514/maps
000000400000-000000401000 r--p 00000000 fd:01 26477890    /home/zack/tiny
000000401000-000000402000 r-xp 00001000 fd:01 26477890    /home/zack/tiny
7fff5c9b3000-7fff5c9d4000 rw-p 00000000 00:00 0           [stack]
7fff5c9fb000-7fff5c9fe000 r--p 00000000 00:00 0           [vvar]
7fff5c9fe000-7fff5c9ff000 r-xp 00000000 00:00 0           [vdso]
$ (unset $(printenv | cut -d= -f1); ulimit -s 4; exec ./tiny)
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  14293 zack      20   0      28      4      0 R 100.0   0.0   0:06.02 tiny
7ffdf80e5000-7ffdf80e6000 rw-p 00000000 00:00 0              [stack]
$ (ulimit -s 4; exec ./tiny)
Segmentation fault