33

如果我在运行时重新编译可执行文件会发生什么?操作系统是否在开始运行时将所有可执行文件的内容读入内存,因此它永远不会读取新的可执行文件?或者它会读取新的可执行文件的部分,认为它没有改变,导致可能未定义的行为?

如果我有一个正在运行的脚本,它在循环中重复调用一个可执行文件,并且我在脚本运行时重新编译该可执行文件,该怎么办。是否保证循环的未来迭代将调用新的可执行文件,并且只有在进行切换时正在进行的调用结果可能被破坏?

我的操作系统是 Linux,但我也很好奇 Windows 上会发生什么。

4

8 回答 8

36

由于这是一个传统的编译器,它会写出一个可执行文件,让我们在 Linux 中跟随它。

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

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

因此,如果您重新编译并创建一个新的同名可执行文件,则不会影响正在运行的进程。它将继续使用旧的可执行文件。任何试图打开文件的新进程都将获得新进程。如果你有system("foo");一个循环,每次执行它都会看到文件名 foo 的意思。

Windows 以不同的方式处理文件。一般来说,如果有进程使用文件,则该文件被锁定,不能删除或替换。

于 2010-07-28T19:43:59.020 回答
8

这取决于。

如果操作系统将整个可执行文件读入内存并且不引用磁盘映像,那么是的,您可以在它“正在使用”时重新编译它。

在实践中,这并不总是发生。如果操作系统在可执行文件上保持文件句柄打开(像 Windows 一样),这将防止文件被删除和/或覆盖。

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

于 2010-07-28T19:11:58.790 回答
7

在 Windows 中,您无法删除锁定的文件,但大多数人不知道的是,您可以移动或重命名正在运行的 exe。

所以你可以

  • 将旧 exe 移动到同一驱动器上的临时目录
  • 安排在下次重新启动时将其删除:MoveFileEx(name, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
  • 移动一个新的 exe 在它的位置。

旧程序将继续运行,但新进程将使用新文件。

于 2010-07-28T20:16:20.830 回答
6

在 Linux 下,可执行文件根据需要被请求分页到内存中。磁盘上的可执行文件成为应用程序的后备存储。这意味着您无法修改磁盘上的可执行文件,否则您将影响正在运行的应用程序。如果您尝试open(2)使用正在使用的可执行文件进行写入,您将收到ETXTBSY(文本文件繁忙)错误(请查看手册页open(2))。

正如许多其他人所说,您可以从文件系统中删除该文件unlink(2)(文件)。这意味着您可以通过首先删除它然后创建一个与旧文件同名的新文件来有效地“覆盖”正在使用的可执行文件。

因此,它归结为编译器在“覆盖”现有文件时如何创建可执行文件。如果它只是打开文件进行写入并截断它(O_WRONLY|O_CREAT|O_TRUNC),它将失败并出现ETXTBSY错误。如果它首先删除现有的输出文件并创建一个新文件,它将正常工作。

于 2010-07-29T00:13:00.053 回答
0

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

于 2010-07-28T19:12:02.500 回答
0

这取决于。根据我的经验,在 Linux 上,如果你删除它,你仍然可以运行一个程序(而且它不是太大)。但我不认为这是定义的行为。

就循环而言,根据您调用可执行文件的方式,您可能会在执行仅编写一半的程序时导致脚本崩溃。

于 2010-07-28T19:12:11.847 回答
0

在 Windows 中,如果可执行文件仍在运行,则文件将被锁定。如果 exe 实际上没有运行,新的运行应该会选择新的,这取决于你的脚本是如何编码的。

我不知道Linux。

于 2010-07-28T19:13:14.870 回答
-1

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

由于操作系统假定程序的文件仍然存在,因此没有理由将这些内存块实际写入交换文件。所以它们只是被无效和重用。如果程序再次需要这些页面,操作系统会从可执行文件中加载它们。

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

不确定Linux,但IIRC它以相同的方式进行交换。

于 2010-07-28T19:24:11.777 回答