Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
File io 在非事务文件系统中实现原子文件写入_File Io_Transactions_Filesystems_Atomicity - Fatal编程技术网

File io 在非事务文件系统中实现原子文件写入

File io 在非事务文件系统中实现原子文件写入,file-io,transactions,filesystems,atomicity,File Io,Transactions,Filesystems,Atomicity,许多常见的文件系统不提供原子操作,但在某些情况下,以原子方式编写文件非常重要。我试图想出解决这个问题的办法 我做了以下假设: 正在使用的文件系统支持inode级别的原子操作(例如,NTFS)。这意味着移动和删除是原子的 只有程序本身才能访问这些文件 一次只有一个程序实例,它以单线程方式运行 为简单起见,每次都会写入整个文件内容(即截断写入) 这就留下了以下问题:在编写文件时,程序可能会被中断,文件只剩下部分内容要写 我提议以下进程: 将新内容写入临时文件新建 将原始文件原始移动到临时备份位置

许多常见的文件系统不提供原子操作,但在某些情况下,以原子方式编写文件非常重要。我试图想出解决这个问题的办法

我做了以下假设:

  • 正在使用的文件系统支持inode级别的原子操作(例如,NTFS)。这意味着移动和删除是原子的
  • 只有程序本身才能访问这些文件
  • 一次只有一个程序实例,它以单线程方式运行
  • 为简单起见,每次都会写入整个文件内容(即截断写入)
这就留下了以下问题:在编写文件时,程序可能会被中断,文件只剩下部分内容要写

我提议以下进程:

  • 将新内容写入临时文件新建
  • 将原始文件原始移动到临时备份位置
  • 把新的移到原来的
  • 删除备份
  • 新文件和备份文件可以与原始文件区分开来(例如,它们的前缀可以不同,也可以位于同一卷上的单独目录中)。同时,它们的名称应该直接映射到相应的原始文件(例如,只需使用相同的文件名)

    然而,这并没有使操作原子化。该过程可以通过步骤1、2、3或4中断:

  • 留下一个可能不完整的新文档
  • 移动是原子的,但目标文件现在已丢失。新的和备份都存在并已完成
  • 移动是原子的,但有一个未使用的备份。原来的内容被新内容取代了
  • 删除是原子的
  • 使用前面的假设2和3,程序在崩溃后必须重新启动。在启动过程中,它应执行以下恢复检查:

    • 如果新的存在,但备份不存在,则我们在步骤1中或之后崩溃。删除新的,因为它可能不完整
    • 如果存在新的,并且备份也存在,那么我们在第2步之后崩溃了。继续执行步骤3
    • 如果备份存在,但新的也不存在,则我们在第3步后崩溃。继续执行步骤4
    仅使用原子操作的恢复过程本身将在中断后停止的地方继续

    我相信这个想法可以确保为单个程序编写原子代码。这些问题仍然存在:

    • 使用同一程序的多个实例时,恢复过程会干扰其他程序中当前正在进行的文件写入
    • 只读不写的外部程序通常会得到正确的结果,但如果同时对请求的条目执行写入操作,它们可能会错误地找不到条目
    这些问题(前面的假设排除了这些问题)可以通过使用策略解决(例如,检查其他实例,并拒绝其他用户访问目录)


    最后,我的问题是:这有意义吗,还是过程中存在缺陷?是否有任何问题妨碍了这种方法在实践中的应用?

    您的步骤可以进一步简化:

  • 将新内容写入临时文件新建
  • 删除原始文件
  • 把新的移到原来的
  • 启动时:

  • 如果原始文件不存在,但新文件存在,则该过程在步骤3“将新文件移到原始文件”之前中断
  • 如果“原始”和“新”都存在,则该过程在步骤2“删除新”之前中断

  • 我曾在管理配置文件时使用过此方法,但在此过程中从未遇到过问题。

    您只需假设一件事,重命名文件是原子操作

    因此,以下步骤将确保纠正(至少在类unix操作系统上)

  • 将新内容写入临时文件新建
  • 将临时文件重命名为原始名称

  • 这样,如果应用程序在重新启动时崩溃,它可以在不需要额外代码的情况下获取旧内容或新内容。

    我为此编写了一些C#代码,我可以根据请求添加它,但问题已经有一英里长了。这不起作用,因为必须删除旧文件才能启用重命名。@mafurt:not true;重命名可能会覆盖(至少在Unix上)。警告:某些文件系统不保证在提交元数据之前将数据块写入磁盘。在这种情况下,您可能必须在写入临时文件后但在重命名之前调用
    fdatasync()
    或等效程序。您假定指定的操作按顺序进行。并不是每个文件系统都能保证这一点。@CodeInChaos:这太糟糕了:(所以我必须在步骤之间刷新fs缓冲区?继续你的想法,我听说硬盘会忽略刷新请求。。。