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