最近用ansible部署一个分布式三节点的应用,发现ansible.builtin.copy
task 用于替换更新可执行文件的时候从来没遇到Text file busy
按理说可执行文件运行中替换文件偶尔会ETXTBSY于是我猜测ansible是先cp到临时文件再mv
Q: 能不能改进下我以下的bash脚本,先 rsync 拷贝文件,如果遇到 text file busy 再 systemctl stop 重新拷贝,或者你有没有建议?例如read copy update 这样先拷贝到tmp再mv过去
你提到的脚本基本上是一个连续的过程,其中文件同步(通过rsync)和远程服务管理(通过systemctl)是依次执行的。你希望能对脚本进行改进,以便在rsync遇到“text file busy”错误时,停止服务后重试。
首先,rsync通常不会返回特定的错误代码来指示“text file busy”问题,因此你可能需要检查rsync命令的输出
你提到的“先拷贝到tmp再mv过去”的方法也是一个可行的策略,因为mv命令通常更不容易遇到“text file busy”的问题。
这里使用的mv -f会强制覆盖目标位置的文件,所以请确保这是你想要的行为。对于这种方法来说,没有必要停止和启动服务,因为mv操作通常是原子的,不会被“text file busy”影响。
Q: 等等,为什么cp操作会遇到text file busy但是mv不会
(ETXTBSY) 是一个错误条件,通常发生在你试图写入或删除一个正在被执行的文件时。这是操作系统为了防止正在运行的程序的代码在执行时被修改而引入的保护机制
你使用 mv 命令将文件从一个位置移动到另一个位置,并且移动发生在同一个文件系统内,mv 操作实际上是重命名了文件的inode(索引节点)。这意味着文件数据本身并没有被移动或修改,而只是更新了文件系统的目录结构。因此,这个操作不会被 ETXTBSY 错误所影响
Q: 我有一个疑惑是rust可执行文件是个elf应该是运行时加载到内存中的,为什么操作系统要设置ETXTBSY的错误机制,防止rust可执行文件运行中替换可执行文件呢
ETXTBSY
错误是 UNIX 和类 UNIX 系统的一个特性,它不仅仅专门针对 Rust 可执行文件;它适用于所有正在执行的可执行文件。这种错误机制的存在是为了防止在一个程序执行的同时更改其代码。这包括 Rust 编写的程序、C 编写的程序,乃至任何可执行的 ELF(Executable and Linkable Format)文件。
这个机制的设置是出于以下原因:
-
一致性和稳定性:当一个程序正在运行时,它的代码会被加载到内存中。如果允许在运行时对磁盘上的可执行文件进行更改,那么程序的行为可能变得不可预测,因为它的代码和数据可能变得不一致。
-
安全性:防止修改正在执行的代码可以帮助防止某些类型的攻击,例如替换或篡改正在执行的程序来执行恶意代码。
-
数据完整性:保护正在执行的可执行文件不被修改,确保了程序的完整性和可靠性。
在许多情况下,当需要更新或替换正在运行的程序时,最佳实践是首先停止该程序,然后进行必要的文件操作,最后重新启动程序。
Q: 但我要保证远程主机部署的rust进程能快速替换更新,所以必须是scp更新替换可执行文件再重启这样服务停机时间最短,你提到mv不会触发ETXTBSY确实不错,解决了我之前多次scp被ETXTBSY报错无法替换必须先停止后替换的烦恼。例如我这样scp替换运行中的应用可执行文件是一个很安全没有任何问题的操作,很烦恼linux系统偶尔会ETXTBSY阻止我
虽然在同一文件系统内使用 mv
命令进行文件替换通常不会触发 ETXTBSY
错误,但需要特别注意的是,这种做法有一定的风险,尤其是在没有停止服务的情况下替换可执行文件:
-
内存映射文件:当程序运行时,它的可执行文件一部分或全部被映射到内存中。如果可执行文件在磁盘上被替换,运行中的程序可能会因为指向旧执行代码的内存映射而继续运行旧的代码或者变得不稳定。
-
文件描述符:如果运行中的程序打开了自己的可执行文件(例如日志记录或者自我更新功能),直接替换文件可能导致潜在的文件描述符无效或者指向已经不再存在的旧文件。
为了确保更新过程的安全性和可靠性,你可以使用一种「原子」的更新方法,这通常包括以下步骤:
-
复制文件:将新的可执行文件通过
scp
或rsync
复制到远程主机的临时目录。 -
停止服务:停止当前正在运行的服务以确保所有相关资源被释放。
-
移动文件:使用
mv
命令原子地替换旧的可执行文件。 -
重启服务:启动服务以运行新的可执行文件。
这样虽然服务的停机时间会稍微增加,但这种方法通常更加安全和可靠,因为它避免了在运行中的可执行文件上进行替换所带来的风险。
Q: 等等内存映射文件指的是mmap吗?进程还能把自己可执行文件elf文件当作mmap?
是的,内存映射文件
指的确实是通过系统调用如 mmap
实现的。在许多操作系统上,mmap
系统调用可以用来将磁盘上的文件映射到进程的虚拟地址空间。当这样做时,文件的内容可以像访问普通内存那样被访问,而不需要通过读写文件的系统调用。这通常用于高效的文件I/O操作,以及实现共享内存等功能。
对于大多数现代操作系统来说,当程序启动执行时,它的可执行文件(比如 ELF 格式的文件)是被操作系统映射到进程的地址空间的。这是操作系统加载可执行文件到内存并执行的一部分过程。这种映射是由操作系统自动管理的,不是通过 mmap
系统调用显式发起的,但底层机制类似。
程序在运行时可以调用 mmap
来映射任何文件,包括其自己的可执行文件。这不是常见的做法,因为通常可执行文件的代码段已经被操作系统加载到内存中。但是,在某些特殊情况下,程序可能会这样做,比如为了实现某种形式的优化或是特殊的内存管理技术。此外,程序也可以通过 mmap
映射其他类型的文件,比如用于数据库、图像处理或是大型数据集的访问。
综上所述,当程序运行时,其代码通常是自动被操作系统映射到内存中的。同时,程序也可以通过 mmap
显式地映射其自己的可执行文件或其他文件。无论哪种映射方式,都是操作系统为了提高程序执行效率和方便内存管理而提供的一种机制。