在C中使用静态链接时出现意外的内存使用行为

mpbci0fu  于 2023-06-05  发布在  其他
关注(0)|答案(1)|浏览(255)

我正在研究静态链接和动态链接,我的理解是,与动态链接相比,静态链接消耗更多的内存和磁盘空间。很明显,静态链接占用更多的磁盘空间,因为静态库被编译到每个可执行文件中。但是如何验证静态链接是否消耗更多的内存空间呢?为此,我设计了一个小实验,如下所述。

第一步:创建一个名为mylib. c的C文件

//mylib.c
#include <unistd.h>

// This function is designed to create a massive .text section.
void many_code() {
    asm volatile (
       "movabs $0x1122334455667788, %%rax \n\t"
       "movabs $0x1122334455667788, %%rax \n\t"
       "movabs $0x1122334455667788, %%rax \n\t"  <----- This line is repeated one million times.
       ...
       ::: "rax"
    );
    sleep(-1);
}

第二步:执行gcc -c mylib. c-o mylib.o,将源文件编译成目标文件。
第三步:使用ar -r libmy.a mylib.o命令创建静态库
第四步:创建第二个C文件use_st_lib.c

// use_st_lib.c
extern void many_code();

int main() {
   many_code();
   return 0;
}

第五步:使用静态链接创建可执行文件use_st_lib_0.out,命令为“gcc use_st_lib.c -static libmy.a -o use_st_lib_0”。
第六步:使用静态链接创建第二个可执行文件,命令为“gcc use_st_lib. c-static libmy. a-o use_st_lib_1”。

➜  static git:(master) ✗ ls -hl
total 91M
-rw-r--r-- 1 root root 9.6M May 29 09:37 libmy.a
-rw-r--r-- 1 root root  51M May 27 23:33 mylib.c
-rw-r--r-- 1 root root 9.6M May 29 09:37 mylib.o
-rwxr-xr-x 1 root root  11M May 29 10:11 use_st_lib_0
-rwxr-xr-x 1 root root  11M May 29 10:12 use_st_lib_1
-rw-r--r-- 1 root root   76 May 27 22:10 use_st_lib.c

**第七步:在另一个终端会话中,使用top命令,设置刷新时间为1.0s。使用COMMAND=use_st_lib.**应用筛选条件

top - 10:21:59 up 15 days, 17:40,  4 users,  load average: 0.00, 0.01, 0.05
Tasks:  79 total,   1 running,  78 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.0 us,  1.0 sy,  0.0 ni, 98.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   1756.5 total,    793.8 free,    133.2 used,    829.5 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   1469.8 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND

第八步:使用./use_st_lib_0 &命令在后台运行进程use_st_lib_0。

➜  static git:(master) ✗ ./use_st_lib_0 &
[1] 31239
top - 10:31:06 up 15 days, 17:49,  4 users,  load average: 0.07, 0.06, 0.06
Tasks:  80 total,   1 running,  79 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   1756.5 total,    793.6 free,    133.1 used,    829.8 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   1469.9 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND          
31239 root      25   5   10.6m   9.5m   9.5m S  0.0  0.5   0:00.00 use_st_lib_0

第九步:使用“./use_st_lib_1 &"命令在后台运行进程use_st_lib_1。

➜  static git:(master) ✗ ./use_st_lib_1 &
[2] 31309
top - 10:32:02 up 15 days, 17:50,  4 users,  load average: 0.03, 0.05, 0.05
Tasks:  81 total,   1 running,  80 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  1.0 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   1756.5 total,    793.3 free,    133.4 used,    829.9 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   1469.6 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND          
31309 root      25   5   10.6m   9.5m   9.5m S  0.0  0.5   0:00.00 use_st_lib_1     
31239 root      25   5   10.6m   9.5m   9.5m S  0.0  0.5   0:00.00 use_st_lib_0

以下是我的问题:
1,为什么我的程序在top命令的输出中显示SHR(共享内存)使用情况?(我希望这一列是0。)
2、启动use_st_lib_0后,RES(常驻内存)显示使用量为9.5m,但top命令顶部显示的内存使用量没有增加。(我预计它会从133.2使用改为123.7使用。)
3、类似地,在启动use_st_lib_1之后,top命令的输出显示很少或没有变化。(我预计它会从123. 7使用到114. 2使用。)
4、在上述步骤中,我在哪里犯了错误,或者我的理解可能出现了什么样的偏差?
以上是我所做的尝试和我所期望的结果。

k2arahey

k2arahey1#

我的理解是静态链接比动态链接消耗更多的内存和磁盘空间。
这种理解是非常不完整的。
如果你有一个./a.out二进制文件,当完全静态链接时,这个二进制文件通常会消耗更少的内存和磁盘空间。
这是因为:

  • 只有实际引用的代码和数据才被链接进来(相反,共享库必须链接 * 所有内容 *,无论是否使用)。
  • 不需要空间用于“动态链接支持表”(PLTGOT),也不需要用于动态重定位。这可能会消耗大量的空间。

OTOH,如果你有多个二进制文件,它们都使用 * 相同 * 共享库,并且你同时运行所有这些二进制文件,那么它们使用的总内存和磁盘空间通常会比动态链接小,如果每个二进制文件都是完全静态链接的。
这是因为您不需要将库的副本作为每个可执行文件的一部分,也不需要将该库的代码多次加载到内存中。
另一个复杂之处是Linux使用了 * 按需分页 *,这意味着你的可执行文件不会消耗太多的RAM *,除非 * 它实际上运行了其中的所有代码,访问了数据,等等。
P.S.
使用top通常不是解决进程内存问题的好方法。它的输出可能非常具有误导性。请仔细阅读man top
在Linux上,您可以通过查找/proc/$pid/pagemap(如果您的内核配置了CONFIG_PROC_PAGE_MONITOR)来实际说明物理内存的每一页。

相关问题