File io 独立于平台的文件锁定?

File io 独立于平台的文件锁定?,file-io,concurrency,locking,d,samba,File Io,Concurrency,Locking,D,Samba,我在做一项计算密集型的科学工作,时不时地吐出结果。这项工作基本上就是对同一件事情进行多次模拟,所以它被分成了几个使用不同操作系统的计算机。我希望将所有这些实例的输出定向到同一个文件,因为所有计算机都可以通过NFS/Samba看到同一个文件系统。以下是限制条件: 必须允许安全的并发附件。如果另一台计算机上的其他实例当前正在附加到该文件,则必须阻止 性能不起作用。每个实例的I/O每分钟只有几个字节 简单确实重要。这一点的全部意义(除了纯粹的好奇)是,我可以停止让每个实例写入不同的文件,并手动将这些文

我在做一项计算密集型的科学工作,时不时地吐出结果。这项工作基本上就是对同一件事情进行多次模拟,所以它被分成了几个使用不同操作系统的计算机。我希望将所有这些实例的输出定向到同一个文件,因为所有计算机都可以通过NFS/Samba看到同一个文件系统。以下是限制条件:

  • 必须允许安全的并发附件。如果另一台计算机上的其他实例当前正在附加到该文件,则必须阻止
  • 性能不起作用。每个实例的I/O每分钟只有几个字节
  • 简单确实重要。这一点的全部意义(除了纯粹的好奇)是,我可以停止让每个实例写入不同的文件,并手动将这些文件合并在一起
  • 不能依赖于文件系统的详细信息。必须使用NFS或Samba装载上的未知文件系统

  • 我用的语言是D,以防万一。我已经看过了,标准库中似乎没有这样做的。特定于D的和一般的、语言不可知的答案都是完全可以接受和欣赏的。

    我不知道D,但我认为使用互斥文件来完成这项工作可能会奏效。下面是一些您可能会发现有用的伪代码:

    do {
      // Try to create a new file to use as mutex.
      // If it's already created, it will throw some kind of error.
      mutex = create_file_for_writing('lock_file');
    } while (mutex == null);
    
    // Open your log file and write results
    log_file = open_file_for_reading('the_log_file');
    write(log_file, data);
    close_file(log_file);
    
    close_file(mutex);
    // Free mutex and allow other processes to create the same file.
    delete_file(mutex); 
    

    因此,所有进程都将尝试创建互斥文件,但只有获胜的进程才能继续。写入输出后,关闭并删除互斥锁,以便其他进程也可以这样做。

    在NFS上,客户端缓存和过时数据会出现一些问题。我以前写过一个独立于操作系统的锁模块来在NFS上工作。创建[datafile].锁文件的简单想法在NFS上无法正常工作。解决此问题的基本思路是创建一个锁文件[datafile].lock,如果存在,则表示文件未被锁定,并且希望获取锁的进程将文件重命名为不同的名称,如[datafile].lock.[hostname].[pid]。重命名是一个原子操作,在NFS上运行良好,足以保证锁的独占性。其余的基本上是一系列故障保护、循环、错误检查和锁检索,以防进程在释放锁并将锁文件重命名回[datafile]之前死亡。锁

    经典的解决方案是使用锁文件,或者更准确地说是锁目录。在所有常见的OSs上,创建目录是一项原子操作,因此例程是:

    • 尝试在固定位置创建具有固定名称的锁定目录
    • 如果创建失败,请等待一秒钟左右,然后重试-重复,直到成功
    • 将数据写入真实数据文件
    • 删除锁目录

    许多平台上的应用程序(如CVS)已经使用了多年。唯一的问题出现在罕见的情况下,当您的应用程序在写入时和解除锁定之前崩溃。

    扭曲锁定文件。

    与前面提到的其他答案一样,最简单的方法是在与数据文件相同的目录中创建一个锁文件

    由于您希望能够通过多台PC访问同一个文件,因此我能想到的最佳解决方案是只包含当前写入数据文件的机器的标识符

    因此,写入数据文件的顺序是:

  • 检查是否存在锁定文件

  • 如果有锁文件,请检查其内容是否具有我的标识符,以确定是否是我拥有它。
    如果是这种情况,只需写入数据文件,然后删除锁定文件。
    如果不是这样,只需等待一秒钟或一小段随机时间,然后再次尝试整个循环

  • 如果没有锁文件,请使用我的标识符创建一个锁文件,然后再次尝试整个循环以避免竞争条件(重新检查锁文件是否确实是我的)

  • 与标识符一起,我将在锁文件中记录一个时间戳,并检查它是否早于给定的超时值。
    如果时间戳太旧,则假定锁文件已过时,只需将其删除,因为这意味着写入数据文件的某台PC可能已崩溃或其连接可能已丢失

    另一种解决方案

    如果您控制数据文件的格式,可以在文件开头保留一个结构,以记录文件是否锁定。
    例如,如果仅为此保留一个字节,则可以假定
    00
    表示数据文件未锁定,其他值表示当前写入该文件的机器的标识符

    NFS问题

    好的,我添加了一些东西,因为Jiri Klouda正确地指出,这将导致实际锁文件处于不确定状态

    解决此问题的几种方法:

    • 使用
      noac
      sync
      选项装载NFS目录。这很容易,但并不能完全保证客户端和服务器之间的数据一致性,因此可能仍然存在问题,尽管在您的情况下,这可能是正常的

    • 使用
      O_DIRECT
      O_SYNC
      O_DSYNC
      属性打开锁定文件或数据文件。这将完全禁用缓存。
      这会降低性能,但会确保一致性

    • 您可以使用
      flock()
      锁定数据文件,但其实现不稳定,您需要检查您的特定操作系统是否实际使用NFS锁定服务。否则它可能什么也做不了。
      如果数据文件被锁定,则另一个客户端打开它进行写入将失败。
      哦,是的,它似乎对SMB共享不起作用,所以最好还是忘掉它

    • 不要使用NFS,而是使用Samba:有一个原因,为什么NFS可能不是您使用场景的最佳答案