如何在NFS上进行正确的文件锁定?

2024-04-28 21:07:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图在python 3x和linux/macOS中实现一个“记录管理器”类。这个类相对简单明了,我唯一想要的“困难”是能够在多个进程上访问同一个文件(保存结果的地方)。

从概念上讲,这似乎相当简单:保存时,获取文件的独占锁。更新您的信息,保存新信息,释放文件的独占锁。很简单。

我正在使用fcntl.lockf(file, fcntl.LOCK_EX)获取独占锁。问题是,在互联网上,我发现很多不同网站的说这是不可靠的,它在windows上不起作用,NFS上的支持是不稳定的,macOS和linux之间的情况可能会发生变化。

我已经承认这些代码在windows上是行不通的,但我希望能够在macOS(单机)和linux(多台NFS服务器)上运行。

问题是,我似乎无法让它正常工作;经过一段时间的调试,在macOS上通过了测试之后,我在NFS上用linux(ubuntu 16.04)进行了测试,结果失败了。问题是由多个进程保存的信息之间不一致-某些进程缺少其修改,这意味着锁定和保存过程中出现问题。

我确信我做错了什么,我怀疑这可能与我在网上看到的问题有关。那么,如何处理通过NFS对macOS和linux上的同一文件的多重访问呢?

编辑

这就是将新信息写入磁盘的典型方法:

sf = open(self._save_file_path, 'rb+')
try:
    fcntl.lockf(sf, fcntl.LOCK_EX)  # acquire an exclusive lock - only one writer
    self._raw_update(sf) #updates the records from file (other processes may have modified it)
    self._saved_records[name] = new_info
    self._raw_save() #does not check for locks (but does *not* release the lock on self._save_file_path)
finally:
    sf.flush()
    os.fsync(sf.fileno()) #forcing the OS to write to disk
    sf.close() #release the lock and close

虽然这是一个典型的方法,它只从磁盘读取信息,看起来像:

sf = open(self._save_file_path, 'rb')
try:
    fcntl.lockf(sf, fcntl.LOCK_SH)  # acquire shared lock - multiple writers
    self._raw_update(sf) #updates the records from file (other processes may have modified it)
    return self._saved_records
finally:
    sf.close() #release the lock and close

另外,这就是原始保存的样子:

def _raw_save(self):
    #write to temp file first to avoid accidental corruption of information. 
    #os.replace is guaranteed to be an atomic operation in POSIX
    with open('temp_file', 'wb') as p:
        p.write(self._saved_records)
    os.replace('temp_file', self._save_file_path) #pretty sure this does not release the lock

错误消息

我已经编写了一个单元测试,其中我创建了100个不同的进程,50个读取,50个写入同一个文件。每个进程都会进行一些随机等待,以避免按顺序访问文件。

问题是有些记录没有保存;最后有3-4个随机记录丢失,所以我只得到46-47个记录,而不是50个。

编辑2

我修改了上面的代码,获得的锁不是文件本身,而是一个单独的锁文件。这可以防止关闭文件会释放锁(如@janneb所建议的那样)的问题,并使代码在mac上正常工作。但是,同样的代码在带有NFS的linux上失败了。


Tags: 文件thetoself信息lock进程linux