最近在做内存优化,发现了一个现象,一个共享库,在一个进程中,不占用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