我正在运行一项计算量很大的科学工作,不时地吐出结果。这项工作基本上只是为了模拟相同的事情,所以它分为几台使用不同操作系统的计算机。我想将所有这些实例的输出定向到同一个文件,因为所有计算机都可以通过NFS / Samba看到相同的文件系统。以下是约束条件:
我使用的语言是D,如果重要的话。我看,标准库中似乎没有任何东西可以做到这一点。 D语言和一般语言无关的答案都是完全可以接受和赞赏的。
答案 0 :(得分:7)
在NFS上,您遇到了客户端缓存和陈旧数据的一些问题。我之前已经编写了一个独立于操作系统的锁模块来处理NFS。创建[datafile] .lock文件的简单想法在NFS上不起作用。解决它的基本思想是创建一个锁文件[datafile] .lock,如果存在意味着文件未被锁定,并且想要获取锁的进程将文件重命名为不同的名称,如[datafile] .lock。[主机名]。[PID]。重命名是一个足够原子的操作,在NFS上运行良好,可以保证锁的独占性。其余的基本上是一堆故障安全,循环,错误检查和锁定检索,以防进程在释放锁定并将锁定文件重命名回[datafile] .lock
之前死亡。答案 1 :(得分:2)
经典的解决方案是使用锁定文件,或者更准确地说是锁定目录。在创建目录的所有常见操作系统上都是原子操作,因此例程是:
许多平台已经被CVS等应用程序使用了很多年。唯一的问题是在您的应用程序在写入时和删除锁定之前崩溃的极少数情况下。
答案 2 :(得分:2)
扭曲锁定文件
与其他提到的答案一样,最简单的方法是在与数据文件相同的目录中创建一个锁文件。
由于您希望能够通过多台PC访问同一文件,我能想到的最佳解决方案是只包含当前正在写入数据文件的机器的标识符。
因此,写入数据文件的顺序是:
检查是否存在锁定文件
如果有锁文件,请检查其内容是否有我的标识符,看看我是否拥有该文件。
如果是这种情况,只需写入数据文件,然后删除锁定文件
如果不是这种情况,只需等待一秒钟或一小段随机时间再次尝试整个循环。
如果没有锁定文件,请使用我的标识符创建一个并再次尝试整个循环以避免竞争条件(重新检查锁定文件是否真的是我的)。
除了标识符,我还会在锁定文件中记录一个时间戳,并检查它是否比给定的超时值更旧。
如果时间戳太旧,则假设锁定文件是陈旧的,只是删除它,因为它会写入数据文件的PC之一可能已崩溃或其连接可能已丢失。
另一种解决方案
如果你控制数据文件的格式,可以在文件的开头保留一个结构来记录它是否被锁定。
如果你只是为此目的保留一个字节,你可以假设,例如,00
意味着数据文件没有被锁定,而其他值则代表当前写入它的机器的标识符。 / p>
NFS问题
好的,我添加了一些东西,因为Jiri Klouda正确地指出NFS uses client-side caching会导致实际的锁定文件处于未确定状态。
解决此问题的几种方法:
使用noac
或sync
选项挂载NFS目录。这很容易,但并不能完全保证客户端和服务器之间的数据一致性,但是可能仍然存在问题,尽管在您的情况下可能没问题。
使用O_DIRECT
,O_SYNC
或O_DSYNC
属性打开锁定文件或数据文件。这应该完全禁用缓存
这会降低性能,但会确保一致性。
您可能能够使用flock()
来锁定数据文件,但其实现参差不齐,您需要检查您的特定操作系统是否实际使用了NFS锁定服务。否则它可能什么都不做
如果数据文件被锁定,则另一个打开它进行写入的客户端将失败
哦,是的,它似乎不适用于SMB股票,所以最好不要忘记它。
不要使用NFS而只使用Samba:有一个good article on the subject以及为什么NFS可能不是您使用场景的最佳答案。
您还将在本文中找到用于锁定文件的各种方法。
Jiri的解决方案也很好。
基本上,如果您想保持简单,请不要将NFS用于在多台计算机之间共享的频繁更新的文件。
不同的东西
使用小型数据库服务器将数据保存到并完全绕过NFS / SMB锁定问题,或者保留当前的多个数据文件系统,并编写一个小实用程序来连接结果。
它可能仍然是解决您问题的最安全,最简单的方法。
答案 3 :(得分:2)
为什么不构建一个位于文件和其他计算机之间的简单服务器?
然后,如果您想更改数据格式,则只需修改服务器,而不是所有客户端。
在我看来,构建服务器比尝试使用网络文件系统要容易得多。
答案 4 :(得分:1)
我不知道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);
因此,所有进程都会尝试创建互斥文件,但只有获胜者才能继续。编写输出后,关闭并删除互斥锁,以便其他进程也可以这样做。