Python : 锁定 NFS 上的文本文件

Python : Locking text file on NFS

我在服务器上有一个文件 results.txt,多个虚拟机通过 NFS 访问该文件。每个这些 VM 上的进程 运行s 读取 results.txt 文件并修改它。如果两个进程 AB 同时读取文件,那么根据进程写入的顺序,A 或 B 的修改将出现在 results.txt 中文件。

如果进程 A 对文件有写锁,那么进程 B 必须等到锁被释放才能读取 results.txt 文件。

我尝试使用 Python:

来实现这个
import fcntl


f = open("/path/result.txt")
fcntl.flock(f,fcntl.LOCK_EX)
#code

对于本地磁盘上的文件,它按预期工作。

但是当我 运行 尝试锁定挂载路径上的文件时,出现以下错误:

Traceback (most recent call last):
  File "lock.py", line 12, in <module>
    fcntl.flock(f,fcntl.LOCK_EX)
IOError: [Errno 45] Operation not supported 

我尝试了 fcntl.fcntlfcntl.flock 但得到了同样的错误。这是我使用 fcntl 的方式的问题吗?存储文件的服务器是否需要配置?

编辑:

我就是这样使用的 fcntl.fcntl:

f= open("results.txt")
lockdata = struct.pack('hhllhh', fcntl.F_RDLCK,0,0,0,0,0)
rv = fcntl.fcntl(f, fcntl.F_SETLKW, lockdata)

NFS 服务器版本为 3。

我发现 flufl.lock 最适合我的要求。

引用项目page的作者:

[...] O_EXCL is broken on NFS file systems, programs which rely on it for performing locking tasks will contain a race condition. The solution for performing atomic file locking using a lockfile is to create a unique file on the same fs (e.g., incorporating hostname and pid), use link(2) to make a link to the lockfile. If link() returns 0, the lock is successful. Otherwise, use stat(2) on the unique file to check if its link count has increased to 2, in which case the lock is also successful.

因为它不是标准库的一部分,所以我不能使用它。此外,我的要求只是该模块提供的所有功能的一个子集。

以下功能是基于模块编写的。请根据要求进行修改。

def lockfile(target,link,timeout=300):                                             
        global lock_owner                                                          
        poll_time=10                                                               
        while timeout > 0:                                                         
                try:                                                               
                        os.link(target,link)                                       
                        print("Lock acquired")                                      
                        lock_owner=True                                            
                        break                                                      
                except OSError as err:                                             
                        if err.errno == errno.EEXIST:                              
                                print("Lock unavailable. Waiting for 10 seconds...")
                                time.sleep(poll_time)                              
                                timeout-=poll_time                                 
                        else:                                                      
                                raise err                                          
        else:                                                                      
                print("Timed out waiting for the lock.") 

def releaselock(link):                          
        try:                                    
                if lock_owner:                  
                        os.unlink(link)         
                        print("File unlocked")   
        except OSError:                         
                print("Error:didn't possess lock.")

这是一个适合我的粗略实现。我一直在使用它,没有遇到任何问题。虽然有很多事情可以改进。希望这可以帮助。