在学习C的同时做了一些实验,我遇到了一些奇怪的事情。这是我的计划:
int main(void) {sleep(5);}
编译时,可执行文件的文件大小为8496字节(与26字节源相比!)这是可以理解的,因为调用了sleep并且调用它的指令写在可执行文件中。另一个要点是,没有睡眠,可执行文件变为4312字节。
int main(void) {}
我的主要问题是第一个程序运行时会发生什么。我正在使用clang编译和Mac OS X来运行它。结果(根据Activity Monitor)是程序使用504KB的“真实内存”。当程序只有4KB时为什么这么大?我假设可执行文件已加载到内存中但除了睡眠调用之外我没有做任何事情。为什么我的程序需要500KB才能睡5秒钟?
顺便说一句,我使用sleep的原因是首先能够使用Activity Monitor捕获正在使用的内存量。
我只是出于好奇而欢呼,欢呼!
答案 0 :(得分:7)
编译C程序时,它会链接到可执行文件中。即使您的程序非常小,它也会链接到C运行时,其中包含一些额外的代码。可能存在一些错误处理,并且此错误处理可能会写入控制台,此代码可能包含sprintf
,这会为您的应用程序增加一些空间。您可以请求链接器生成可执行文件中的代码映射,以查看实际包含的内容。
此外,可执行文件包含的不仅仅是机器代码。将有各种数据和动态链接表,这将增加可执行文件的大小,并且可能还有一些浪费的空间,因为各个部分存储在块中。
C运行时将在调用main
之前初始化,这将导致某些代码被加载(例如,通过动态链接到各种操作系统功能)以及为堆分配的内存,堆栈每个线程,也可能还有一些静态数据。并非所有这些数据都显示为“真实内存” - OS X上的默认堆栈大小似乎为8 MB,而您的应用程序仍然使用的数据远远少于此。
答案 1 :(得分:2)
在这种情况下,我认为您观察到的尺寸差异是由动态链接引起的。
链接器通常不会将公共代码放入可执行二进制文件中,而是保留信息,并在加载二进制文件时加载代码。这些公共代码存储在名为shared object(SO)
或dynamically linked library(DLL)
的文件中。
[pengyu@GLaDOS temp]$ cat test.c
int main(void) {sleep(5);}
[pengyu@GLaDOS temp]$ gcc test.c
[pengyu@GLaDOS temp]$ du -h --apparent-size a.out
6.6K a.out
[pengyu@GLaDOS temp]$ gcc test.c -static
[pengyu@GLaDOS temp]$ du -h --apparent-size a.out
807K a.out
此外,我在这里列出了流程内存中的内容:
这里ldd
给出了在调用二进制文件时要加载的动态库的结果。这些库位于通过调用mmap
系统调用获得它的部分。
[pengyu@GLaDOS temp]$ cat test.c
int main(void) {sleep(5);}
[pengyu@GLaDOS temp]$ gcc test.c
[pengyu@GLaDOS temp]$ ldd ./a.out
linux-vdso.so.1 (0x00007fff576df000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f547a212000)
/lib64/ld-linux-x86-64.so.2 (0x00007f547a5bd000)
.data
,.code
等部分。这部分存在于二进制可执行文件中,因此大小应该不比文件本身大。在可执行二进制文件的加载阶段复制的内容。
.bss
这样的部分以及要在程序执行期间动态使用的堆栈区域。这部分在二进制可执行文件中不存在,因此大小可能非常大而不受文件本身大小的影响。