覆盖可执行文件是否会影响正在运行原始可执行文件的进程?

覆盖可执行文件是否会影响正在运行原始可执行文件的进程?

当进程中运行可执行文件时,如果可执行文件被覆盖或删除,然后通过重新安装重新创建,进程是否会重新运行新的可执行文件?

问题的答案取决于

  • 可执行文件是否作为进程中的服务/守护进程运行?

  • 操作系统,例如 Linux、Unix,...?

  • 重新安装是通过安装程序文件(例如debUbuntu、msiWindows 上的文件)还是通过构建其源代码进行?

这里有些例子:

  • 在Ubuntu中,当一个进程运行一个可执行文件时,当我通过手动重新安装、在其源代码上覆盖该可执行文件时configuremakemake install进程仍然继续运行原始可执行文件,而不是新的可执行文件。

  • 我听说在Windwos 10中,当一个进程将一个可执行文件作为服务运行时,如果我们通过其msi安装程序文件重新安装该可执行文件,那么该服务进程将重新启动以运行新的可执行文件。在 Ubuntu 或 Debian 上从 .deb 文件安装是否有相同或相似的情况?

谢谢。

答案1

它取决于内核和可执行文件的类型。它不取决于可执行文件的启动或安装方式。

在 Linux 上:

  • 对于本机可执行文件(即包含机器代码的二进制文件,由内核直接执行),可执行文件在运行时无法修改。

    $ cp /bin/sleep .
    $ ./sleep 999999 &
    $ echo >sleep
    sh: 1: cannot create sleep: Text file busy
    

    可以删除可执行文件(即取消链接)并在同一路径创建一个新可执行文件。与文件仍处于打开状态时被删除的任何其他情况一样,删除可执行文件不会影响正在运行的进程,并且实际上不会将其从磁盘中删除,直到该文件不再使用,即直到所有正在运行的实例程序退出。

  • 对于脚本(以 开头#!),可以在程序运行时修改脚本文件。这是否影响程序取决于解释器如何读取脚本。如果它在开始执行之前将整个脚本读入自己的内存中,那么执行将不会受到影响。如果解释器按需读取脚本那么执行可能会受到影响;的一些实现可以sh做到这一点。

许多其他 Unix 系统都有这种行为,但不是全部。 IIRC 旧版本的 Solaris 允许修改本机可执行文件,这通常会导致其崩溃。一些 Unix 变体(包括 HP/UX)甚至不允许删除当前正在运行的本机可执行文件。

大多数软件安装程序都会在安装新可执行文件之前先删除现有的可执行文件,而不是覆盖现有的二进制文件。例如做

rm /bin/target
cp target /bin

而不仅仅是cp target /bin。 shell命令install就是这样做的。但这并不理想,因为如果有人尝试在进程运行/bin/target时执行,他们会得到一个损坏的程序。cp最好将文件复制到一个临时名称,然后将其重命名为最终名称。重命名文件(即,将其移动到同一目录中,或者更一般地,将其移动到同一文件系统中)会删除先前的目标文件(如果存在)。dpkg例如,这就是工作原理。

cp target /bin/target.tmp
mv /bin/target.tmp /bin/target

答案2

你可以自己尝试一下:

$ cp /usr/bin/sleep /tmp/sleep
$ /tmp/sleep 20 &
$ truncate -s 1 /tmp/sleep
truncate: cannot open '/tmp/sleep' for writing: Text file busy

系统不允许您更改正在运行的二进制文件。但是,您可以取消链接该文件并更改它:

$ /tmp/sleep 20 &
$ rm /tmp/sleep
$ cp /usr/bin/ls /tmp/sleep
$ [2]-  Done                    /tmp/sleep 20

请注意,对于 shell 脚本,内核不会保护脚本,因为繁忙的二进制文件是 /bin/bash。您不得覆盖 shell 脚本文件,但可以将其删除并用新文件替换。

至于更新包的安装,取决于打包器是否重新启动正在运行的守护进程。我不知道是否有约定,但我查看了 Fedora 系统上的一些示例 rpm scriptlet,它们似乎在更新时重新启动。例如,

$ rpm --scripts -qf /usr/sbin/xinetd 
...
postuninstall scriptlet (using /bin/sh):
...
if [ $1 -ge 1 ] ; then 
    # Package upgrade, not uninstall 
    systemctl try-restart xinetd.service >/dev/null 2>&1 || : 
fi

rpm -U软件包的升级将运行新的安装前和安装后 scriptlet,然后运行旧的卸载前和卸载后 scriptlet 。如上所示,卸载后将重新启动 systemd 服务。


从评论中,还可以查看这个回答在共享库上,并注意如何更改解释脚本在很大程度上取决于解释器如何缓冲或重新读取文件,或将其即时重新编译为新文件。

答案3

覆盖可执行文件是否会影响正在运行原始可执行文件的进程?

通常不会,我的理解是,当内核分叉并执行新进程时,可执行文件(包含程序)被读取并加载到内存中。
通常不再需要底层文件,至少直到下次为另一个进程读取该文件为止。
这就是为什么您需要在更新替换系统程序或库的基础文件后重新启动计算机 - 您不能“影响运行进程”(即替换)为从更新文件中读取的新版本。
获得新的、更新的功能的唯一方法是停止正在运行的进程并从新文件重新加载它,但对于系统程序或内核本身,程序无法关闭,就像您可以使用一个浏览器。因此,必须关闭整个系统才能读取新的更新文件。

相关内容