mar*_*trz 48 memory libraries executable ram
我想知道 Linux 管理共享库的方式。(实际上,我指的是 Maemo Fremantle,它是 2009 年发布的基于 Debian 的发行版,运行在 256MB RAM 上)。
假设我们有两个可执行文件链接到 libQtCore.so.4 并使用其符号(使用其类和函数)。为简单起见,我们称它们为a和b。我们假设两个可执行文件都链接到相同的库。
首先我们启动a. 必须加载库。它是整体加载还是仅在需要的部分加载到内存中(因为我们不使用每个类,只加载有关所用类的代码)?
然后我们启动b. 我们假设它a仍在运行。b也链接到 libQtCore.so.4 并使用一些使用的类a,但也使用a. 库是否会被双重加载(分别为a和单独为b)?或者他们会使用 RAM 中已有的相同对象。如果不b使用新符号并且a已经在运行,共享库使用的 RAM 会增加吗?(或者差异会微不足道)
der*_*ert 67
注意:我将假设您的机器具有内存映射单元 (MMU)。有一个不需要 MMU 的 Linux 版本 (µClinux),这个答案在那里不适用。
什么是 MMU?它是硬件——处理器和/或内存控制器的一部分。了解共享库链接不需要您确切了解 MMU 的工作原理,只要 MMU 允许逻辑内存地址(程序使用的地址)和物理内存地址之间存在差异内存地址(内存总线上实际存在的地址)。内存被分解成页,在 Linux 上的大小通常为 4K。对于 4k 页,逻辑地址 0–4095 是页 0,逻辑地址 4096–8191 是页 1,等等。MMU 将这些映射到 RAM 的物理页,每个逻辑页通常可以映射到 0 或 1 个物理页。一个给定的物理页可以对应多个逻辑页(这就是内存的共享方式:多个逻辑页对应同一个物理页)。请注意,无论操作系统如何,这都适用;这是对硬件的描述。
在进程切换时,内核会更改 MMU 页面映射,以便每个进程都有自己的空间。进程 1000 中的地址 4096 可以(并且通常是)与进程 1001 中的地址 4096 完全不同。
几乎每当您看到一个地址时,它就是一个逻辑地址。用户空间程序几乎从不处理物理地址。
现在,也有多种方法可以构建库。假设一个程序调用foo()库中的函数。CPU 对符号或函数调用一无所知——它只知道如何跳转到一个逻辑地址,并执行它在那里找到的任何代码。有几种方法可以做到这一点(当图书馆访问自己的全局数据等时,类似的事情也适用):
几乎没有人再使用 #1,至少不会在通用系统上使用。在 32 位系统上保持唯一的逻辑地址列表是不可能的(没有足够的空间),而在 64 位系统上则是管理上的噩梦。但是,预链接是在每个系统的基础上执行此操作的。
使用 #2 还是 #3 取决于库是否是使用 GCC -fPIC(位置无关代码)选项构建的。#2 没有,#3 有。通常,库是用 构建的-fPIC,所以 #3 是会发生的。
有关更多详细信息,请参阅 Ulrich Drepper 的如何编写共享库 (PDF)。
所以,最后,你的问题可以得到回答:
-fPIC(几乎肯定应该是)构建的,那么对于加载它的每个进程,绝大多数页面都是完全相同的。您的流程a和b可能装载在不同的逻辑地址库中,但那些将指向同一个物理页:内存将被共享。此外,RAM 中的数据与磁盘上的数据完全匹配,因此只能在页面错误处理程序需要时加载。-fPIC. 因此,它们必须是单独的物理页(因为它们包含不同的数据)。这意味着它们不共享。这些页面与磁盘上的内容不匹配,因此如果加载了整个库,我不会感到惊讶。它当然可以随后被换出到磁盘(在交换文件中)。您可以使用该pmap工具进行检查,也可以直接通过检查/proc. 例如,这里是pmap -x两个不同的新生成的bcs的(部分)输出。请注意, pmap 显示的地址是典型的逻辑地址:
pmap -x 14739
Address Kbytes RSS Dirty Mode Mapping
00007f81803ac000 244 176 0 r-x-- libreadline.so.6.2
00007f81803e9000 2048 0 0 ----- libreadline.so.6.2
00007f81805e9000 8 8 8 r---- libreadline.so.6.2
00007f81805eb000 24 24 24 rw--- libreadline.so.6.2
pmap -x 17739
Address Kbytes RSS Dirty Mode Mapping
00007f784dc77000 244 176 0 r-x-- libreadline.so.6.2
00007f784dcb4000 2048 0 0 ----- libreadline.so.6.2
00007f784deb4000 8 8 8 r---- libreadline.so.6.2
00007f784deb6000 24 24 24 rw--- libreadline.so.6.2
Run Code Online (Sandbox Code Playgroud)
您可以看到该库分多个部分加载,并分别pmap -x为您提供详细信息。您会注意到两个进程的逻辑地址不同;您可以合理地期望它们是相同的(因为它运行的是相同的程序,并且计算机通常是可预测的),但是有一个称为地址空间布局随机化的安全功能,可以有意地将它们随机化。
从大小(Kbytes)和常驻大小(RSS)的差异可以看出整个库段还没有被加载。最后,您可以看到对于较大的映射,dirty 为 0,这意味着它与磁盘上的内容完全对应。
您可以使用 重新运行pmap -XX,它会根据您正在运行的内核版本向您显示,因为 -XX 输出因内核版本而异——第一个映射的 aShared_Clean为 176,与RSS. Shared内存意味着物理页面在多个进程之间共享,并且由于它匹配 RSS,这意味着内存中的所有库都是共享的(查看下面的另请参阅以进一步解释共享与私有):
pmap -XX 17739
Address Perm Offset Device Inode Size Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous AnonHugePages Swap KernelPageSize MMUPageSize Locked VmFlagsMapping
7f784dc77000 r-xp 00000000 fd:00 1837043 244 176 19 176 0 0 0 176 0 0 0 4 4 0 rd ex mr mw me sd libreadline.so.6.2
7f784dcb4000 ---p 0003d000 fd:00 1837043 2048 0 0 0 0 0 0 0 0 0 0 4 4 0 mr mw me sd libreadline.so.6.2
7f784deb4000 r--p 0003d000 fd:00 1837043 8 8 8 0 0 0 8 8 8 0 0 4 4 0 rd mr mw me ac sd libreadline.so.6.2
7f784deb6000 rw-p 0003f000 fd:00 1837043 24 24 24 0 0 0 24 24 24 0 0 4 4 0 rd wr mr mw me ac sd libreadline.so.6.2
Run Code Online (Sandbox Code Playgroud)
也可以看看