如果操作系统具有共享库的单个副本,那么为什么安装新的共享库不会影响当前正在运行的进程?
首先,您的整个单一副本的概念有点不妥。
让我们来谈谈一个ELF共享库(这些概念也适用于其他类型的库,尽管细节有所不同)。
一个ELF共享库通常会有至少两个可加载段:一个只读文件和一个可写文件。第一个包含只读数据,程序代码(通常称为.text
),重定位部分等。第二个段包含已初始化但可写的数据(通常称为.data
)。
当两个进程中运行,并使用相同libfoo.so
,将有至少页由libfoo.so
用于存储:至少一个页为“覆盖”的只读段(即页面将在之间共享两个运行过程)以及至少一个单独的页的每个过程以“覆盖”可写段。
正如你由此可以看出,共享库的磁盘上单个副本,而库由正在运行的程序也被复制成多个副本在RAM。
二,我们需要谈谈如何更新libfoo.so
。你可以做到这一点在以下两种方法之一:
- 你可以这样做:
rm -f libfoo.so; gcc -shared -o libfoo.so foo.o
,或
- 你可以这样做:
gcc -shared -o libfoo.so foo.o
。
在第一种情况下,你是不是影响有mmap
版libfoo.so
在所有的任何过程:老的数据libfoo.so
将保持在磁盘上,但它没有它已经任何过程不可见open
ed或mmap
ed。另请注意,如果libfoo.so
的大小为1GB,则磁盘使用量将增加1GB(旧的和新的拷贝仍占用磁盘空间)。
在第二种情况下,您正在更新libfoo.so
到位(这不建议使用,原因很快会变得明显)。 libfoo.so
的inode编号将保持不变,旧数据将消失。您的磁盘使用率将保持不变(假设新的libfoo.so
与旧的大小大致相同)。
这将影响任何正在运行的进程,但可能不会以您期望的方式。最可能的结果是你的运行过程会崩溃。
为什么会这样?把图书馆想象成一本书,里面有目录。在初始库加载期间,目录将被加载到RAM中,并且被修改为(因为共享库可以被加载到内存中的任意位置)。如果您现在以如下方式更新本书(磁盘上的库):第3章变长了3页,那么目录将不再有效(至少在第4章到最后)。任何试图遵循目录中的指针的尝试都不会将你引向你正在寻找的章节的开头,而是会在一章的中间。所以你可以调用一个函数,并在不同的函数中着陆。最可能的结果是崩溃。
图片更复杂demand paging。您可能在某些章节中已经分页,但不是其他章节。所以你可能不会发现你的过程实际上在更新之后立即被解决了。如果你的图书馆很小,你可能不会发现在所有。
P.S.某些操作系统禁止第二种形式的更新:如果某个进程正在使用该库,则打开库进行写入操作将失败,并返回ETXTBSY
。 Linux为某些文件系统执行此操作,但不是全部。
我非常肯定,如果不是所有的大多数.so都在运行时加载到内存中。 – chrisd1100
你期望什么?为什么?定义“影响”的含义。 – Olaf
“影响”表示已加载的库实例的代码将被修改的库或修改的库所替换,将在内存中具有单独的实例。 –