重建和安装共享库不会影响已经加载该库的进程

Rebuilding & install Shared library does not impact on process which already loaded that library

我对多个进程使用的共享库有疑问。

我有一个共享库 libfoo.so,它被两个不同的进程使用,process1process2

第一个进程 (process1) 处于 运行 状态并且 libfoo.so 已加载到内存中。我对 libfoo.so 代码进行了一些修改,重建并安装,然后启动 process2。新的process2加载了新安装的库libfoo.so.

但是 process1 仍然 运行 年长 libfoo.so。如果我重新启动 process1,那么它会按预期加载新安装的 libfoo.so

如果操作系统只有一个共享库副本,那么为什么安装新的共享库不会影响当前 运行 个进程?

If the operating system has a single copy of shared library, then why does installing a new shared library not affect currently running processes?

首先,您关于单个副本的整个概念有些缺陷。

我们来谈谈 ELF 共享库(这些概念也适用于其他类型的库,尽管细节有所不同)。

一个 ELF 共享库通常 至少 两个可加载段:一个只读段和一个 writable 段。第一个包含只读数据、程序代码(通常称为.text)、重定位段等。第二个段包含已初始化但写入table 的数据(通常称为.data)。

当两个进程 运行 并使用相同的 libfoo.so 时,libfoo.so 将至少使用 页内存:至少有一页到 "cover" 只读段(该页将在两个 运行ing 进程之间共享),并且至少有一个 separate每个 处理到"cover" writable 段。

从这里可以看出,磁盘磁盘上的共享库的单个副本也被复制到内存RAM中的多个副本,而该库由 运行ning 程序使用。

其次,我们需要谈谈你如何更新libfoo.so。您可以通过以下两种方式之一进行操作:

  1. 你可以这样做: rm -f libfoo.so; gcc -shared -o libfoo.so foo.o,或
  2. 你可以这样做:gcc -shared -o libfoo.so foo.o.

在第一种情况下,您根本不会影响任何已 mmaped libfoo.so 的进程:libfoo.so 的旧数据将 保留 在磁盘上,但对任何尚未 opened 或 mmaped 的进程不可见。另请注意,如果 libfoo.so 的大小为 1GB,您的磁盘使用量将增加 1GB(旧副本和新副本仍在占用磁盘空间 space)。

在第二种情况下,您正在 libfoo.so 就地 更新(不推荐这样做,原因很快就会变得明显)。 libfoo.so 的 inode 编号将保持不变,旧数据将消失。您的磁盘使用量将保持不变(假设新 libfoo.so 与旧磁盘的大小大致相同)。

影响任何 运行ning 进程,但可能不会以您期望的方式出现。最有可能的结果是您的 运行ning 进程将崩溃。

为什么会这样?将图书馆想象成一本书,其中包含 table 的内容。在初始库加载期间,table 的内容将被加载到 RAM 中,并且 被修改(因为共享库可以加载到内存中的任意位置)。如果您现在更新图书(磁盘 上的图书馆 ),例如第 3 章长了 3 页,然后 table 的内容将不再有效(至少对于第 4 章到最后)。任何尝试遵循 table 内容中的指针的尝试都不会让您到达您正在寻找的章节的开头,而是在章节的中间。所以你会调用一个函数,然后进入另一个函数的中间。最有可能的结果是崩溃。

画面更复杂demand paging。您可能翻阅过某些章节,但没有翻阅过其他章节。因此,您可能不会发现您的进程实际上在更新后立即被清理。如果您的图书馆很小,您可能根本 .

找不到这个

P.S。一些操作系统禁止第二种形式的更新:如果某个进程当前正在使用该库,则打开该库进行写入会失败并显示 ETXTBSY。 Linux 对某些文件系统这样做,但不是全部。