如果我在运行时重新编译可执行文件会发生什么?操作系统在开始运行时是否将所有可执行文件的内容读入内存,因此它永远不会读取新的可执行文件?或者它会读取新的可执行文件的部分,认为它没有改变,导致可能是未定义的行为?
如果我运行的脚本在循环中重复调用可执行文件,并且在脚本运行时重新编译可执行文件,该怎么办?是否可以保证循环的未来迭代将调用新的可执行文件,并且只有在进行切换时正在进行的调用的结果可能已损坏?
我的操作系统是Linux,但我也对Windows上发生的事情感到好奇。
答案 0 :(得分:28)
由于这是一个传统的编译器,它写出了一个可执行文件,让我们在Linux中使用它。
首先要知道的是Linux文件名并不直接引用该文件,而是引用与文件名无关的目录条目。文件实际上并不需要文件名,但如果不是,则很难引用它。
如果某个进程正在使用某个文件,并且您替换或删除该文件,则该进程将通过其目录条目继续使用该文件。使用该文件或查找它的任何新进程都将获得新版本(如果您替换它)或找不到它(如果您删除它)。一旦所有进程都通过旧文件,它将从文件系统中删除。
因此,如果重新编译并创建同名的新可执行文件,则不会影响正在运行的进程。它将继续使用旧的可执行文件。尝试打开文件的任何新进程都将获得新进程。如果你在一个循环中有system("foo");
,那么每次执行它时都会看到文件名foo的意思。
Windows以不同方式处理文件。通常,如果有使用文件的进程,则该文件被锁定,可能不会被删除或替换。
答案 1 :(得分:7)
取决于。
如果操作系统将整个可执行文件读入内存并且没有返回磁盘映像,那么可以在“正在使用”时重新编译它。
在实践中,这并不总是发生。如果操作系统在可执行文件上保持文件句柄打开(就像Windows一样),这将阻止文件被删除和/或覆盖。
使用Linux / Unix,可以覆盖“正在使用”的文件。有关详细解释,请参阅David Thornley的答案。
答案 2 :(得分:5)
在Windows中,您无法删除锁定的文件,但大多数人不知道的是您可以移动或重命名正在运行的exe文件。
所以你可以
旧程序将继续运行,但新进程将使用新文件。
答案 3 :(得分:4)
在Linux下,可执行文件需要根据需要分页到内存中。磁盘上的可执行文件成为应用程序的后备存储。这意味着您无法修改磁盘上的可执行文件,否则将影响正在运行的应用程序。如果您尝试open(2)
正在使用的可执行文件进行编写,则会出现ETXTBSY
(文本文件繁忙)错误(请查看open(2)
的手册页。)
正如许多其他人所说,您可以从文件系统(unlink(2)
)中删除该文件,内核将保留对它的引用,而不是从磁盘中删除它,直到没有更多引用(当进程退出时) ,它将释放其对文件的引用)。这意味着您可以通过首先删除它,然后创建一个与旧文件同名的新文件来有效地“覆盖”正在使用的可执行文件。
因此,它归结为编译器在“覆盖”现有文件时如何创建可执行文件。如果它只是打开文件进行写入并截断它(O_WRONLY|O_CREAT|O_TRUNC
),它将失败并出现ETXTBSY
错误。如果它首先删除现有的输出文件并创建一个新文件,它将正常工作。
答案 4 :(得分:0)
我认为它不会让你替换文件,因为Windows在使用时将其锁定。
答案 5 :(得分:0)
这取决于。根据我的经验,在Linux上你可以运行一个程序,如果你删除它(并且它不是太大)。但我不认为这是定义的行为。
就循环而言,取决于你如何调用可执行文件,当你执行一个只写了一半的程序时,你可能会崩溃你的脚本。
答案 6 :(得分:0)
在Windows中,如果可执行文件仍在运行,则无法锁定该文件。如果exe实际上没有运行,则新运行应该选择新运行,具体取决于脚本编码的方式等。
我不了解Linux。
答案 7 :(得分:-1)
可执行文件可能在启动时被完全加载到内存中,但是如果它足够大并且运行时间足够长,操作系统可能会决定换掉它的一些未使用的部分。
由于操作系统假定程序的文件仍然存在,因此没有理由将这些内存块实际写入交换文件。所以它们只是无效并重复使用。如果程序再次需要这些页面,则OS会从可执行文件中加载它们。
在Windows中,这实际上是自动发生的,因为加载的模块是内存映射文件。这也意味着文件在执行期间被锁定,您将无法轻易覆盖它。
不确定Linux,但IIRC以相同的方式进行交换。