重建和安装共享库不会影响已经加载该库的进程
Rebuilding & install Shared library does not impact on process which already loaded that library
我对多个进程使用的共享库有疑问。
我有一个共享库 libfoo.so
,它被两个不同的进程使用,process1
和 process2
。
第一个进程 (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
。您可以通过以下两种方式之一进行操作:
- 你可以这样做:
rm -f libfoo.so; gcc -shared -o libfoo.so foo.o
,或
- 你可以这样做:
gcc -shared -o libfoo.so foo.o
.
在第一种情况下,您根本不会影响任何已 mmap
ed libfoo.so
的进程:libfoo.so
的旧数据将 保留 在磁盘上,但对任何尚未 open
ed 或 mmap
ed 的进程不可见。另请注意,如果 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 对某些文件系统这样做,但不是全部。
我对多个进程使用的共享库有疑问。
我有一个共享库 libfoo.so
,它被两个不同的进程使用,process1
和 process2
。
第一个进程 (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
。您可以通过以下两种方式之一进行操作:
- 你可以这样做:
rm -f libfoo.so; gcc -shared -o libfoo.so foo.o
,或 - 你可以这样做:
gcc -shared -o libfoo.so foo.o
.
在第一种情况下,您根本不会影响任何已 mmap
ed libfoo.so
的进程:libfoo.so
的旧数据将 保留 在磁盘上,但对任何尚未 open
ed 或 mmap
ed 的进程不可见。另请注意,如果 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 对某些文件系统这样做,但不是全部。