Python:在 NFS 上锁定文本文件

gag*_*nso 6 python nfs file-locking fcntl python-2.7

results.txt在服务器上有一个文件,多个 VM 通过 NFS 访问该文件。一个进程在每个 VM 上运行,它读取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
Run Code Online (Sandbox Code Playgroud)

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

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

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

我试过了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)
Run Code Online (Sandbox Code Playgroud)

NFS 服务器版本为 3。

gag*_*nso 5

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

引用作者从项目页面

[...] O_EXCL 在 NFS 文件系统上被破坏,依赖它来执行锁定任务的程序将包含竞争条件。使用锁文件执行原子文件锁定的解决方案是在同一个文件系统上创建一个唯一的文件(例如,合并主机名和pid),使用link(2) 建立到锁文件的链接。如果link()返回0,则锁定成功。否则,对唯一文件使用 stat(2) 检查其链接计数是否已增加到 2,在这种情况下锁定也成功。

由于它不是标准库的一部分,我无法使用它。另外,我的要求只是该模块提供的所有功能的子集。

根据模块编写了以下功能。请根据要求进行修改。

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.")
Run Code Online (Sandbox Code Playgroud)

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