为何Linux共享库.so加载到内存后体积比磁盘文件更大?
我开始更多地在Linux环境下编写代码,想要熟悉与Windows差异较大的Linux环境及API。在研究Linux共享库so(对应Windows的.dll)时,发现某共享库加载到内存后,体积比磁盘上的文件大了7752字节。我原本预期磁盘镜像与内存镜像一致,怀疑是否是演示代码存在问题?
以下是示例输出:
Program returned: 0
Program stdout
Loaded: linux-vdso.so.1
Loaded: /lib/x86_64-linux-gnu/libc.so.6
---------------------MEMORY-------------------------
libc.so.6 size: 2037344 bytes
7fb5733e4000 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 .ELF............
7fb5733e4010 03 00 3e 00 01 00 00 00 c0 41 02 00 00 00 00 00 ..>......A......
7fb5733e4020 40 00 00 00 00 00 00 00 18 e7 1e 00 00 00 00 00 @...............
...
7fb5735d5630 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
7fb5735d5640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
7fb5735d5650 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
---------------------DISK-------------------------
libc.so.6 size: 2029592 bytes
7fb5731f2010 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 .ELF............
7fb5731f2020 03 00 3e 00 01 00 00 00 c0 41 02 00 00 00 00 00 ..>......A......
7fb5731f2030 40 00 00 00 00 00 00 00 18 e7 1e 00 00 00 00 00 @...............
...
7fb5733e17f8 00 00 00 00 00 00 00 00 c8 e2 1e 00 00 00 00 00 ................
7fb5733e1808 4b 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 K...............
7fb5733e1818 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
核心疑问
为何共享库在磁盘与内存中的体积存在差异?
这种差异是Linux共享库加载机制的正常表现,主要源于以下几点:
内存页对齐机制
Linux内存管理以页为最小单位(多数系统是4KB),共享库的每个加载段都会被对齐到内存页的边界。磁盘上的ELF文件为了节省空间,段之间是紧密排列的;但加载到内存后,哪怕段的实际大小没填满一页,系统也会分配完整的页,这部分填充的空白字节直接导致内存中的总尺寸变大。动态链接的额外开销
共享库加载时,系统会处理全局偏移表(GOT)、过程链接表(PLT)的重定位,填充实际的内存地址。此外,内核还会为共享库的加载状态管理分配少量额外内存,这些都会让内存中的镜像比磁盘文件多出部分字节。ELF文件的节与段转换
磁盘上的ELF文件按“节(Section)”组织,其中部分仅用于编译调试的节(比如符号表、调试信息)不会被加载到内存;但反过来,内核会将多个功能相关的节合并为“段(Segment)”加载,内存中可能包含磁盘文件里没有的动态链接临时结构。尺寸统计方式差异
你用来统计内存中大小的代码,大概率是计算了共享库加载起始地址到结束地址的完整内存区间——这个区间包含了所有对齐后的内存页;而磁盘文件的大小是实际存储的字节数,两者统计逻辑不同,并非演示代码存在问题。
内容的提问来源于Stack Exchange,提问作者vengy

