Linux上的C中的原子文件复制(用新版本替换文件并保留旧版本的副本)

a9wyjsp7  于 2023-08-03  发布在  Linux
关注(0)|答案(4)|浏览(95)

我正在尝试实现一个原子版本的写时复制。我有一定的条件,如果满足,将使原始文件的副本。
我实现了类似于这个伪代码的东西。

//write operations//
if(some condition)
   //create a temp file//
   rename(srcfile, copied-version)
   rename(tmpfile, srcfile)

字符串
这个逻辑的问题:
硬链接。我想把硬链接从复制的版本转移到新的srcfile。

nom7f22z

nom7f22z1#

你不能这样。
硬链接是一个方向性的指针。因此,您无法修改或删除您不明确了解的其他硬链接。您所能做的就是将数据写入相同的文件,而这不是原子的。
此规则统一适用于hadlink和file descriptors。这意味着您不能修改未知硬链接指向的内容,也不能在同一文件打开的情况下修改另一个进程指向的内容。
这样可以有效地防止您以原子方式修改未知硬链接指向的文件。
如果您可以控制可能修改或访问这些文件的每个进程(如果这些文件只被您编写的程序修改),那么您可能能够使用flock()向其他进程发出该文件正在使用的信号。如果文件存储在NFS远程文件系统上,则此操作不起作用,但通常在其他情况下应起作用。

6yt4nkrj

6yt4nkrj2#

在某些情况下,file leases可以解决底层问题(确保原子内容更新),但前提是每个读取器和写入器都为每个快照打开和关闭文件。
由于传统的复制-更新-重命名序列也存在类似的限制,因此文件租赁解决方案可能也适用于OP。
有关详细信息,请参见man 2 fcntlLeasesManaging signals部分。进程必须与文件具有相同的所有者,或者具有CAP_LEASE能力(通常通过文件系统能力授予进程)。超级用户进程(以root身份运行)默认具有此功能。
这个想法是,当进程希望对文件进行“原子”更改时,它会获得对文件的写租约。This only succeeds if no other process has the file open.如果另一个进程试图打开该文件,租约保持器会收到一个信号,并有最长的租约中断时间(通常大约一分钟)来降级租约(或简单地关闭该文件);在此期间,开启器将阻塞。
请注意,没有办法转移开瓶器。情况是打开器已经有了底层inode的句柄(所以访问检查和文件名解析已经发生);只是内核在释放或中断租用之前不会将其返回给用户空间进程。
但是,您的租约所有者可以为临时文件创建当前内容的副本,同时获取该文件的写租约,然后使用目标文件名对其进行重命名。这样,每个(组)打开器获得文件内容的句柄,就像它们在打开时一样;如果他们做了任何修改,它们将是“私有的”,并且不会反映在原始文件上。由于底层inode不再被任何文件名引用,当它们(最后一个打开它的进程)关闭它时,inode被删除,存储被释放回文件系统。Linux页面缓存也能很好地缓存这样的访问,所以在很多情况下,“临时复制文件”甚至不会命中实际的存储介质(除非有内存压力,即非页面缓存目的所需的存储器)。
纯粹的“原子修改”不需要任何类型的副本或重命名,仅在必须对读取器表现为原子的一组写入的持续时间内保持租约。
请注意,采用写租约通常会阻塞,直到没有其他进程再打开该文件,因此这种基于租约的原子更新发生的时间受到限制,并且不能保证始终可用。(例如,您可能有一个惰性进程,它只是保持文件打开,偶尔轮询它。如果您有这样的流程,这种基于租用的方法将不起作用,但复制-重命名方法也不起作用。)
此外,租约只适用于本地文件。
如果你需要基于记录的原子性,只需要使用基于fcntl的记录锁,让所有的读取器为他们想要原子访问的区域获取一个读锁,所有的写入器为要更新的区域获取一个写锁,因为记录锁是建议的(即,不阻止读取或写入,只阻止其他记录锁)。

gj3fmq9x

gj3fmq9x3#

第4.4节
一起绘制所有子路径的内部。的任何子路径
open在被填充之前是隐式关闭的。
169
路径构建和绘制
如果子路径是退化的(由单点闭合路径或相同坐标处的两个或更多个点组成),则f绘制位于该点下方的单个设备像素;结果是依赖于设备的,并且通常没有用。单点开放子路径(由尾随的m运算符指定)不产生输出。
对于一条简单的路径,直观上很清楚什么区域位于内部。但是,对于更复杂的路径(例如,一个与自身相交的路径或一个子路径包含另一个子路径),“inside”的解释并不总是显而易见的。路径机器使用以下两个规则之一来确定哪些点位于路径内:非零绕组数规则和奇偶规则,这两个规则都将在下面详细讨论。
非零缠绕数规则比奇偶规则更通用,并且是f运算符使用的标准规则。类似地,W操作符使用此规则来确定当前剪切路径的内部。奇偶规则有时对特殊效果或与其它图形系统的兼容性有用; f* 和W* 运算符调用该规则。
非零绕组数规则
非零缠绕数规则通过概念性地绘制从该点到无穷大的任何方向的射线,然后检查路径的一段与射线交叉的地方,来确定给定点是否在路径内。以计数0开始,规则在每次路径段从左到右穿过射线时加1,并且在每次段从右到左穿过射线时减1。在计算所有交叉点之后,如果结果为0,则该点在路径之外;否则它就在里面。
注意:刚刚描述的方法没有指定如果路径段与所选光线相切或相切,该怎么做。由于光线的方向是任意的,因此该规则简单地选择不遇到这种问题交点的光线。
对于简单的凸路径,非零缠绕数规则定义了内部和外部,正如人们直观地期望的那样。更有趣的情况是那些涉及复杂或自相交路径的情况,如图4.10所示。对于一条由星星组成的路径,用五条相连的直线绘制

zengzsys

zengzsys4#

我想转移硬链接
硬链接可不是这么用的它们不是目录条目之间的链接,而是从路径名到inode的链接。除非您知道哪些其他目录条目是指向此inode的链接(并且也可以更改它们以指向新的inode),否则您将不得不修改现有inode的文件内容。菲利普的回答也说明了同样的问题。
您的逻辑也不能自动替换srcfile。在rename调用之间,没有srcfile

如果您没有不可能的硬链接要求,请使用Linux renameat2RENAME_EXCHANGE将新编写的文件与原始srcfile交换。

或者只使用可移植的POSIX调用,link()srcfilecopied-version,然后renametmpfilesrcfile来原子地替换它,所以永远不会有另一个进程可以在srcfile上获得ENOENT的瞬间。

相关问题