在可执行文件运行时重新编译它是否安全?

35

如果我在运行可执行文件时重新编译它会发生什么?操作系统在启动运行该文件时是否会将所有的内容都读入内存,因此它永远不会读取新的可执行文件?还是它会读取新的可执行文件中认为未更改的部分,从而引起可能未定义的行为?

如果我有一个脚本在循环中重复调用一个可执行文件,并且在脚本运行时重新编译了这个可执行文件。那么可以保证循环的未来迭代将调用新的可执行文件,只有在进行切换时正在进行中的调用结果可能会受到损坏吗?

我的操作系统是Linux,但我也想知道在Windows上会发生什么。


1
哪种语言和实现?对于大多数Common Lisp编译器来说,在运行时更改可执行文件没有问题。其他系统的工作方式不同。 - David Thornley
这是一个C++可执行文件。虽然我不确定这对操作系统有什么影响,但它确实是一个可执行文件。 - HighCommander4
1
这很重要,因为编译和执行是完全不同的步骤,所以这取决于操作系统如何处理可执行文件。 - David Thornley
8个回答

38

因为这是一个传统的编译器,它会生成一个可执行文件,让我们在Linux上跟随它。

首先要知道的是,Linux文件名并不直接指向文件,而是指向一个目录条目,这个目录条目与文件名无关。实际上,一个文件并不需要有文件名,但如果没有文件名将很难引用它。

如果一个进程正在使用一个文件,而你替换或删除了它,该进程将通过它的目录条目继续使用该文件。任何新的进程使用该文件或查找它都将得到新版本(如果您替换了它)或找不到它(如果你删除了它)。一旦所有进程都完成了对旧文件的操作,它将从文件系统中删除。

因此,如果你重新编译并创建了一个同名的新可执行文件,你不会影响正在运行的进程。它将继续使用旧的可执行文件。任何尝试打开该文件的新进程都将获得新的可执行文件。如果你在一个循环中有system("foo");,每次执行时它都会看到名称为foo的文件当时代表的内容。

Windows处理文件的方式不同。通常,如果有一个进程在使用文件,则该文件会被锁定,不能被删除或替换。


有趣的 - 这让我对Linux如何处理文件有了一些很好的见解。 - HighCommander4
1
【Linux】它取决于您如何“替换”文件,以确定它是否有效。您无法打开正在使用的可执行文件进行写入。 - camh
如果在链接器输出.exe文件的过程中,我尝试运行它会怎样?它会尝试启动一个半写的可执行文件吗? - Thanatos
1
@Thanatos: 通常,文件将被写入临时名称,然后移动到正确的路径和名称。这意味着您将获得新版本或旧版本之一。如果不是这样,并且它正在原地写出,我怀疑您将无法执行该程序。在正常情况下,您将无法启动半写成的可执行文件。 - David Thornley

8

这要看情况。

如果操作系统将整个可执行文件读入内存并不再引用磁盘镜像,则可以在“使用中”重新编译它。

但实际上,这种情况并不总是发生。如果操作系统保持对可执行文件的文件句柄打开状态(例如Windows),则会阻止文件被删除和/或覆盖。

在Linux / Unix中,可以覆盖“正在使用”的文件。有关详细说明,请参见David Thornley的答案。


7
在Windows中,您无法删除锁定的文件,但大多数人不知道的是,您可以移动或重命名运行中的exe文件。
因此,您可以:
- 将旧的exe文件移动到同一驱动器上的临时目录中 - 安排在下次重新启动时删除它:MoveFileEx(name, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); - 将新的exe文件移到原来的位置。
旧程序将继续运行,但新进程将使用新文件。

4
有趣的信息可以用来展示糟糕的设计选择如何导致奇怪的解决方法。通过将文件信息分为文件名和inode,就不需要这么复杂的命令。此外,无需重新启动即可进行升级。 - Saurabh

6
在Linux下,可执行文件按需分页到内存中。磁盘上的可执行文件成为应用程序的后备存储。这意味着您不能修改磁盘上的可执行文件,否则将影响正在运行的应用程序。如果您尝试以写模式打开(open(2))一个正在使用的可执行文件,则会收到ETXTBSY(文本文件忙)错误(请查看open(2)的man页面)。
正如许多其他人所说,您可以从文件系统中删除该文件(unlink(2)),并且内核将保留对它的引用,并且在没有更多引用时不会从磁盘中删除它(当进程退出时,它将释放对文件的引用)。这意味着您可以通过首先删除它,然后创建与旧文件同名的新文件来有效地“覆盖”正在使用的可执行文件。
因此,问题归结为编译器在“覆盖”现有文件时如何创建可执行文件。如果它只是以写模式打开文件并截断它(O_WRONLY|O_CREAT|O_TRUNC),则会失败并显示ETXTBSY错误。如果它首先删除现有的输出文件并创建一个新文件,则可以在没有错误的情况下工作。

0

我想它不会让你替换文件,因为在使用过程中Windows已将其锁定。


0

这要看情况而定。根据我的经验,在Linux上,如果你删除了一个程序(且它不太大),你仍然可以运行它。但我认为这并不是明确定义的行为。

至于循环,取决于你如何调用可执行文件,当它尝试执行一个只写了一半的程序时,你很可能会导致脚本崩溃。


0
在Windows中,如果可执行文件仍在运行,则该文件将被锁定,因此您无法进行更改。如果实际上没有运行exe文件,则新的运行应该会使用新的文件,这取决于脚本编码方式等其他因素。
我不知道Linux的情况。

-1

可执行文件可能会在启动时完全加载到内存中,但如果它足够大并且运行时间足够长,操作系统可能会决定交换掉一些未使用的部分。

由于操作系统假设程序文件仍然存在,因此实际上没有必要将这些内存块写入交换文件。因此,它们被简单地无效化并重用。如果程序再次需要这些页面,则操作系统从可执行文件中加载它们。

在Windows中,这实际上是自动完成的,因为已加载的模块是内存映射文件。这也意味着在执行期间文件被锁定,您将无法轻松地覆盖它。

不确定Linux是否也是以同样的方式进行交换。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接