![当依赖于共享库的程序正在执行时删除共享库是否安全?](https://linux22.com/image/194568/%E5%BD%93%E4%BE%9D%E8%B5%96%E4%BA%8E%E5%85%B1%E4%BA%AB%E5%BA%93%E7%9A%84%E7%A8%8B%E5%BA%8F%E6%AD%A3%E5%9C%A8%E6%89%A7%E8%A1%8C%E6%97%B6%E5%88%A0%E9%99%A4%E5%85%B1%E4%BA%AB%E5%BA%93%E6%98%AF%E5%90%A6%E5%AE%89%E5%85%A8%EF%BC%9F.png)
rm
是否允许在执行依赖于某个共享库的程序时毫无危险地删除(例如使用命令)某个共享库?我是这么认为的,因为如果程序正在运行,那么库中所有必需的数据都会被加载到内存中
但我注意到,当我重写库(echo 123 > somelib.so
)时,依赖于它的程序会由于以下原因而被破坏:Bus error
如果该库已经在内存中,为什么程序会因为磁盘上的库发生更改而崩溃?而如果库不在内存中,那么为什么可以从磁盘中删除它而程序仍会继续执行呢?它从哪里读取库中的数据?
答案1
一般来说,文件不会实际上当使用 删除它们时,它们会被删除rm
,但只有在它们的所有打开文件句柄也都关闭之后。我很确定这也适用于文件映射到某个进程的内存时。之后rm
,您将无法再次打开该文件,但数据和映射仍然存在。
修改文件是另一回事,因为文件数据只有一个“版本”,所有读取、写入和共享映射都访问相同的数据。如果您在其中写入垃圾,则从该文件运行代码的进程也会得到垃圾。在 Linux 上,您不能对可执行文件执行此操作,它会给出错误(“文本文件忙”),但对共享库没有此类保护。
(您可以尝试:cp -a /bin/cat /tmp/cat; /tmp/cat &
然后尝试echo 123 >> /tmp/cat
。然后fg
返回cat
,然后按 Ctrl-C 将其关闭。)
从技术上讲,可以说这rm
根本没有真正删除文件,它只是删除了文件名。一个文件可以有多个名称,我们称之为硬链接,但它基本上只是同一个文件的多个名称。实际的删除通常是由姓氏被删除而触发的,但它有些独立。
答案2
我是这么认为的,因为如果程序正在运行,那么库中所有必需的数据都会被加载到内存中
读取可执行数据仅在需要时,否则您将等待很长时间才能启动 Google Chrome 等重型应用程序(其二进制文件当前为179 134 080
字节)。此外,Windows 中的 WinRAR 允许创建最大2^64 - 1
大小的自解压存档。如果内核需要在启动之前将所有代码加载到 RAM 中,那么您将无法提取它们。
如果该库已经在内存中,为什么程序会因为磁盘上的库发生更改而崩溃?
它并不完全在内存中,它仅映射到正在执行的进程。当内核决定实际执行库中的代码时,它将被缓存,但在此之前它仅保留在磁盘驱动器上。
即使库已被执行,因此被读取并缓存,并且当内核内存不足时,它仍然可以被缓存。
它崩溃是因为您实际上已经替换了文件并且内核尝试查找您要求执行的函数。一旦文件被覆盖,这些功能就变得垃圾,因此应用程序崩溃。
而如果库不在内存中,那么为什么可以从磁盘中删除它而程序仍会继续执行呢?它从哪里读取库中的数据?
Linux 内核中使用的 Unix 架构允许删除打开的文件。由于该文件的句柄数大于零,因此该文件将不是技术上将其移除,直到最后一个手柄关闭为止。
当依赖于共享库的程序正在执行时删除共享库是否安全?
是的,完美。覆盖这样的库是不安全的。替换它又是安全的,因为内核将使用旧的(已删除的)库。这样您就可以在不重新启动系统的情况下替换几乎所有库。仅当您替换它们时,您才必须重新启动所有正在使用它们的应用程序。
最后,几年前对 Linux 内核进行了修补,以允许动态替换自身:https://ostechnix.com/5-kernel-live-patching-tools-that-will-help-to-run-linux-servers-without-reboots/