ILD

fork前后的内存RSS值差异
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2020-9-18 站点:Inside Linux Development

最近在做内存优化,发现了一个现象,一个共享库,在一个进程中,不占用RSS,但是另外一个进程却占据1M多的RSS,但是另外一个进程却没有使用这个共享库的接口。


为此,写了一个demo程序,啥也不干

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <unistd.h>
 
int main(int argc, char **argv)
{
        while(1)
        sleep(1000);
        return 0;
}



编译命令如下

cc test.c -Wl,--no-as-needed  -L. -lcrypto

-Wl,--no-as-needed,这个编译选项告诉链接器即使共享库没有被引用,也链接他们。


编译后执行,使用meminfo这个工具,查看libcrypto.so这个共享库的RSS,

1
2
3
4
   free swapped     exc     shr     ave
 1468KB     0KB   512KB     0KB     0KB 7f0c6c303000-7f0c6c4f2000 r-xp     0 libcrypto.so.1.0.0
 2048KB     0KB     0KB     0KB     0KB 7f0c6c4f2000-7f0c6c6f2000 ---p 1ef000 libcrypto.so.1.0.0
    0KB     0KB   156KB     0KB     0KB 7f0c6c6f2000-7f0c6c719000 rw-p 1ef000 libcrypto.so.1.0.0

可以看到独占的代码段页有512KB,数据段页有156KB。


使用LD_DEBUG=all,发现

1
$ LD_LIBRARY_PATH=. LD_DEBUG=all ./a.out 2> /work/a.out.ld.log


内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
     15833:    
     15833:    WARNING: Unsupported flag value(s) of 0x8000000 in DT_FLAGS_1.
     15833:    
     15833:    file=libcrypto.so.1.0.0 [0];  needed by ./a.out [0]
     15833:    find library=libcrypto.so.1.0.0 [0]; searching
     15833:     search path=./tls/x86_64/x86_64:./tls/x86_64:./tls/x86_64:./tls:./x86_64/x86_64:./x86_64:./x86_64:.        (LD_LIBRARY_PATH)
     15833:      trying file=./tls/x86_64/x86_64/libcrypto.so.1.0.0
     15833:      trying file=./tls/x86_64/libcrypto.so.1.0.0
     15833:      trying file=./tls/x86_64/libcrypto.so.1.0.0
     15833:      trying file=./tls/libcrypto.so.1.0.0
     15833:      trying file=./x86_64/x86_64/libcrypto.so.1.0.0
     15833:      trying file=./x86_64/libcrypto.so.1.0.0
     15833:      trying file=./x86_64/libcrypto.so.1.0.0
     15833:      trying file=./libcrypto.so.1.0.0
     15833:    
     15833:    file=libcrypto.so.1.0.0 [0];  generating link map
     15833:      dynamic: 0x00007f3060408e50  base: 0x00007f305ffff000   size: 0x00000000004194c0
     1583
     。。。

发现会不停的解析符号,这必然会读取crypto共享库的动态符号表。这样必然导致对应的页被映射。同时发现解析符号时crypto会优先于c库。虽然没有crypto的符号,但是每次都会从crypto尝试查找,于是尝试LD_PRELOAD优先加载c库,发现还是不行。


还尝试了添加 -Wl,-z,lazy,编译选项,尝试懒加载符号,发现也不行。在LD_DEBUG调试那个RSS为0的程序时,发现其实一个daemon程序,会执行fork,顿时我就想明白了。执行fork时,所有的符号都解析完成了。后续根本不会访问crypto的代码段的页。而内核在执行fork时,这些只读的页只是映射了虚拟地址空间,没有映射到物理页,在真正访问的时候才会去映射。


于是修改测试程序代码:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <unistd.h>
 
int main(int argc, char **argv)
{
        if (fork() == 0)
        while(1)
        sleep(1000);
        return 0;
}


编译后,测试:

1
2
3
4
5
$ sudo meminfo `pidof a.out`
   free swapped     exc     shr     ave
 1980KB     0KB     0KB     0KB     0KB 7f40805e0000-7f40807cf000 r-xp     0 libcrypto.so.1.0.0
 2048KB     0KB     0KB     0KB     0KB 7f40807cf000-7f40809cf000 ---p 1ef000 libcrypto.so.1.0.0
    0KB     0KB   156KB     0KB     0KB 7f40809cf000-7f40809f6000 rw-p 1ef000 libcrypto.so.1.0.0


果然,代码段的RSS变成了0,但是数据段不是共享的,会占用RSS。


meminfo工具:

https://insidelinuxdev.net/~yuanjianpeng/project/meminfo


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.