如何在物理内存中避免共享库文本部分的多个副本?

时间:2017-07-19 05:39:51

标签: linux memory-management process shared-libraries loader

当Linux加载共享库时,我的理解是,文本部分只加载到物理内存中一次,然后映射到引用它的不同进程的页表中。

但是,谁/谁确保/检查相同的共享库文本部分未多次加载到物理内存中?

加载程序或mmap()系统调用是否避免了重复,还是有其他方法和方式?

EDIT1: 我必须展示到目前为止所做的工作(研究)。这是......

试图追踪一个简单的睡眠命令。

$ strace sleep 100 &
[1] 22824
$ execve("/bin/sleep", ["sleep", "100"], [/* 26 vars */]) = 0
brk(0)                                  = 0x89bd000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=92360, ...}) = 0
mmap2(NULL, 92360, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7f56000
close(3)                                = 0
open("/lib/libc.so.6", O_RDONLY)        = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0`G\0004\0\0\0"..., 512) = 512
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f55000
fstat64(3, {st_mode=S_IFREG|0755, st_size=1706232, ...}) = 0
mmap2(0x460000, 1426884, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x460000
mmap2(0x5b7000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x156) = 0x5b7000
mmap2(0x5ba000, 9668, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x5ba000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f54000
...
munmap(0xb7f56000, 92360)               = 0
...

然后检查/ proc / pid / maps文件以查找此过程;

$ cat /proc/22824/maps
00441000-0045c000 r-xp 00000000 fd:00 2622360    /lib/ld-2.5.so
...
00460000-005b7000 r-xp 00000000 fd:00 2622361    /lib/libc-2.5.so
...
00e3e000-00e3f000 r-xp 00e3e000 00:00 0          [vdso]
08048000-0807c000 r-xp 00000000 fd:00 5681559    /usr/bin/strace
...

这里可以看到libc.so.6与PROT_READ|PROT_EXEC的mmap2()的addr参数位于特定地址。这让我相信物理内存中的共享库映射是以某种方式由加载器管理的。

1 个答案:

答案 0 :(得分:0)

共享库由PTY.spawn('ruby script.rb') do |reader, writer, pid| reader.expect(/Input 1:/) writer.puts('1') reader.expect(/Input 2:/) writer.puts('2') # Assume exception occurs now, script.sh # prints some 'undefined variable' traceback reader.expect(/Input 3:/) writer.puts('3') end 系统调用加载,Linux内核是智能的。它有一个内部数据结构,它将文件描述符(包含mount实例和inode编号)映射到其中的映射页面。

动态链接器(其代码在mmap()或类似地方)仅使用此/lib/ld-linux.so调用来映射库(然后重新定位其符号表),此页级重复数据删除完全由内核。

映射与mmap()标志一起发生,您可以通过任何工具(例如PROT_READ|PROT_EXEC|PROT_SHARED)轻松检查。