Private_Citizen Asked: 2021-03-08 10:28:00 +0800 CST2021-03-08 10:28:00 +0800 CST 2021-03-08 10:28:00 +0800 CST 在linux中替换活动文件的最佳方法(mv/cp)? 772 我正在制作一个用于更新活动服务的文本包含/conf 文件的脚本。该脚本将首先将更改写入临时文件。完成后,用临时文件替换包含文件,考虑到服务处于活动状态,使用,cp还是其他更好?我不清楚它如何与保留文件句柄的程序一起工作。mvecho > 如果答案是如果有东西持有文件句柄是不可能的,那么最好。假设服务只是打开、读取、关闭。替换文件的最安全方法是什么? 差点忘了,我使用 PHP 作为脚本,使用内置的 php move/copy 方法与 bash mv/有区别cp吗? linux cp 2 个回答 Voted harrymc 2021-03-08T10:59:22+08:002021-03-08T10:59:22+08:00 这里有两个概念:文件名和文件数据。 如果一个文件被 删除rm,它就会失去它的名字,但是直到最后一个打开它的程序停止,它的数据才会被释放。但是,如果文件以独占方式打开,则无法对其进行操作。 因此,如果无法删除该文件,一个已知的解决方法是重命名它。 在您的情况下,如果其他程序不断打开文件,您可能会发现自己处于删除文件但没有复制新文件的情况,因此其他程序会发现文件丢失。 如果丢失的文件不是问题,我会这样做: 将文件重命名为某个临时名称 将替换文件复制到其位置 删除临时名称。 如果此方法不适合您,您将需要使用信号量机制。您可以使用 lockfile 命令作为两个程序之间的锁。这个例子来自man页面: ... lockfile important.lock ... access_"important"_to_your_hearts_content ... rm -f important.lock ... Best Answer Kamil Maciorowski 2021-03-08T14:48:34+08:002021-03-08T14:48:34+08:00 分析 在程序(例如,所讨论的服务)获得文件句柄后,该句柄引用由其在文件系统中的 inode 编号标识的相同文件,该文件包含该文件。文件的路径名无关紧要。 如果文件在文件系统中被重命名或移动,路径名将会改变。如果文件被取消链接(删除)或移动到另一个文件系统(此操作实际上是复制,然后取消链接),路径名将消失。这些都不重要。即使链接计数降至零,句柄也将保持有效(这意味着文件系统中不再有指向该文件的路径名)。只有当链接计数为零并且没有使用该文件时,文件系统才会真正摆脱该文件。 如果您使用whatever … > the_file或向文件写入cp foo the_file内容,则文件的内容将发生变化,但“文件”仍将表示其 inode 编号未更改的文件。旧句柄将引用修改后的文件。握住手柄的程序将能够立即看到更改。 这种方法的一个大问题是程序可能偶然对处于无效状态的文件进行操作。“无效”是指“程序逻辑无效”,而不是“文件系统损坏”。例如>首先截断文件,所以它的大小为零,然后才写入一些东西。程序可能会发现文件为空,但程序的逻辑绝不应该为空。通常,您可以覆盖或修改而不截断或在末尾截断(到所需的大小),因此在某些时候文件可能是一半旧的,一半是新的。无论如何,程序在任何给定时刻都无法轻易知道文件是否完整且有效。当涉及到配置文件时,一种理智的方法是允许程序假定文件是有效的,而不是以这种方式更新配置。 另一种更新方式是使用mv. 当你这样做mv foo the_file并且两者foo都the_file在同一个文件系统中时,the_file会被原子地替换,结果是name 下的foo前者的内容。foothe_file 这里的“原子”意味着如果任何程序the_file在任何时候重新打开,那么它将获得一个文件句柄,该句柄将导致旧内容或新内容。无法发现混合状态或以其他方式损坏的内容,因为此类内容从不存在。 将其视为硬链接foo到the_file(此行为将the_file文件名从其旧 inode 切换到 of foo)然后取消链接foo。在这种情况下,“原子地”并不意味着您不能发现foo和更新的目标文件同时存在。它只是意味着the_file总是要么全旧要么全新,没有中间状态。 笔记: mv在文件系统之间就像cp+ unlink,它的行为就像cp(上面讨论过的)。 请参阅我的 fs 上是mv原子的吗? 因为the_file文件名现在与另一个 inode 相关联(foo在它被取消链接之前指向的那个),the_file从之前的所有文件句柄mv仍然指向旧文件。即使没有路径名,旧文件仍然存在于文件系统中。它可以读取、写入;它可以增长。这是一个单独的文件,与您现在看到的不同the_file。它的不同之处与the_file之前的不同foo。 这意味着如果以这种方式更新由服务保持打开的配置文件将是徒劳的,除非服务通过路径重新打开它并再次读取配置。 在不切换到另一个 inode 的情况下更新时(例如使用cp),您需要一些机制来告诉服务停止使用文件(这可能包括也可能不包括关闭文件)并告诉它稍后继续使用文件。否则存在服务使用文件的某些中间状态的风险。最基本的机制就是停止服务并在准备好时启动。该服务可能支持某种机制来执行此操作而无需停止,但我不会指望它。 一般来说,允许中间状态存在于文件系统中是有风险的。如果发生电源故障或其他中断(例如 rash Ctrl+ C),您可能会得到无效文件。我已经将“无效”解释为“程序逻辑无效”,而不是“在文件系统中损坏”,但后来我们谈论的是文件只是暂时无效的情况,它最终会有效。现在我们正在讨论一种无效状态仍然存在的情况,因为将一个有效状态转换为另一个有效状态的过程在中间终止。 mv允许您避免无效状态。mv你肯定需要一些机制来告诉服务通过路径重新加载文件;否则它将坚持旧文件。最基本的机制是重启服务。该服务可以支持替代方案,例如sshd重新读取其配置SIGHUP,而无需重新启动。这是正道。 注意很多取决于服务的行为方式: 服务可能会读取文件一次,自行配置并且不再读取,除非重新启动或被要求(如果支持)。 或者它有时可能会出于某种原因使用相同的文件句柄再次读取文件。 或者它有时会因为某种原因重新打开而再次阅读。 它可能会监视文件并在文件更改时重新读取它。一般来说,很难判断更改何时完成,所以我会说这种方法是有缺陷的。 该服务可能会监视文件名并在它开始指向另一个 inode 时重新读取它。这适用mv于更新方法,但仅适用于单个文件。如果配置可以存储在许多文件中,那么服务应该只在需要时重新读取它。您可以自动更新任何单个文件,mv但不能更新整个多文件配置。 明确的回答 替换文件的最安全方法是什么? 绝对mv在同一个文件系统中。如果您希望它在不重新启动的情况下使用新文件,则需要该服务的支持。我不知道您的服务表现如何。 使用内置的 PHP 移动/复制方法与 Bash mv/有区别cp吗? (旁注:你在 Bash 中调用的是mv与cpBash 无关的独立可执行文件。) 我不知道PHP,我无法回答这个问题。我认为它的 move 方法没有理由不是原子的,但我可能错了。我希望此答案中的信息对您自己的研究有所帮助。现在你知道要注意什么了。
这里有两个概念:文件名和文件数据。
如果一个文件被 删除
rm
,它就会失去它的名字,但是直到最后一个打开它的程序停止,它的数据才会被释放。但是,如果文件以独占方式打开,则无法对其进行操作。因此,如果无法删除该文件,一个已知的解决方法是重命名它。
在您的情况下,如果其他程序不断打开文件,您可能会发现自己处于删除文件但没有复制新文件的情况,因此其他程序会发现文件丢失。
如果丢失的文件不是问题,我会这样做:
如果此方法不适合您,您将需要使用信号量机制。您可以使用 lockfile 命令作为两个程序之间的锁。这个例子来自
man
页面:分析
在程序(例如,所讨论的服务)获得文件句柄后,该句柄引用由其在文件系统中的 inode 编号标识的相同文件,该文件包含该文件。文件的路径名无关紧要。
如果文件在文件系统中被重命名或移动,路径名将会改变。如果文件被取消链接(删除)或移动到另一个文件系统(此操作实际上是复制,然后取消链接),路径名将消失。这些都不重要。即使链接计数降至零,句柄也将保持有效(这意味着文件系统中不再有指向该文件的路径名)。只有当链接计数为零并且没有使用该文件时,文件系统才会真正摆脱该文件。
如果您使用
whatever … > the_file
或向文件写入cp foo the_file
内容,则文件的内容将发生变化,但“文件”仍将表示其 inode 编号未更改的文件。旧句柄将引用修改后的文件。握住手柄的程序将能够立即看到更改。这种方法的一个大问题是程序可能偶然对处于无效状态的文件进行操作。“无效”是指“程序逻辑无效”,而不是“文件系统损坏”。例如
>
首先截断文件,所以它的大小为零,然后才写入一些东西。程序可能会发现文件为空,但程序的逻辑绝不应该为空。通常,您可以覆盖或修改而不截断或在末尾截断(到所需的大小),因此在某些时候文件可能是一半旧的,一半是新的。无论如何,程序在任何给定时刻都无法轻易知道文件是否完整且有效。当涉及到配置文件时,一种理智的方法是允许程序假定文件是有效的,而不是以这种方式更新配置。另一种更新方式是使用
mv
. 当你这样做mv foo the_file
并且两者foo
都the_file
在同一个文件系统中时,the_file
会被原子地替换,结果是name 下的foo
前者的内容。foo
the_file
这里的“原子”意味着如果任何程序
the_file
在任何时候重新打开,那么它将获得一个文件句柄,该句柄将导致旧内容或新内容。无法发现混合状态或以其他方式损坏的内容,因为此类内容从不存在。将其视为硬链接
foo
到the_file
(此行为将the_file
文件名从其旧 inode 切换到 offoo
)然后取消链接foo
。在这种情况下,“原子地”并不意味着您不能发现foo
和更新的目标文件同时存在。它只是意味着the_file
总是要么全旧要么全新,没有中间状态。笔记:
mv
在文件系统之间就像cp
+unlink
,它的行为就像cp
(上面讨论过的)。mv
原子的吗?因为
the_file
文件名现在与另一个 inode 相关联(foo
在它被取消链接之前指向的那个),the_file
从之前的所有文件句柄mv
仍然指向旧文件。即使没有路径名,旧文件仍然存在于文件系统中。它可以读取、写入;它可以增长。这是一个单独的文件,与您现在看到的不同the_file
。它的不同之处与the_file
之前的不同foo
。这意味着如果以这种方式更新由服务保持打开的配置文件将是徒劳的,除非服务通过路径重新打开它并再次读取配置。
在不切换到另一个 inode 的情况下更新时(例如使用
cp
),您需要一些机制来告诉服务停止使用文件(这可能包括也可能不包括关闭文件)并告诉它稍后继续使用文件。否则存在服务使用文件的某些中间状态的风险。最基本的机制就是停止服务并在准备好时启动。该服务可能支持某种机制来执行此操作而无需停止,但我不会指望它。一般来说,允许中间状态存在于文件系统中是有风险的。如果发生电源故障或其他中断(例如 rash Ctrl+ C),您可能会得到无效文件。我已经将“无效”解释为“程序逻辑无效”,而不是“在文件系统中损坏”,但后来我们谈论的是文件只是暂时无效的情况,它最终会有效。现在我们正在讨论一种无效状态仍然存在的情况,因为将一个有效状态转换为另一个有效状态的过程在中间终止。
mv
允许您避免无效状态。mv
你肯定需要一些机制来告诉服务通过路径重新加载文件;否则它将坚持旧文件。最基本的机制是重启服务。该服务可以支持替代方案,例如sshd
重新读取其配置SIGHUP
,而无需重新启动。这是正道。注意很多取决于服务的行为方式:
mv
于更新方法,但仅适用于单个文件。如果配置可以存储在许多文件中,那么服务应该只在需要时重新读取它。您可以自动更新任何单个文件,mv
但不能更新整个多文件配置。明确的回答
绝对
mv
在同一个文件系统中。如果您希望它在不重新启动的情况下使用新文件,则需要该服务的支持。我不知道您的服务表现如何。(旁注:你在 Bash 中调用的是
mv
与cp
Bash 无关的独立可执行文件。)我不知道PHP,我无法回答这个问题。我认为它的 move 方法没有理由不是原子的,但我可能错了。我希望此答案中的信息对您自己的研究有所帮助。现在你知道要注意什么了。