fcntl文件枷锁模块

发布时间 2023-06-19 16:23:19作者: lxd670

fcntl模块

本模块基于文件描述符来进行文件控制和 I/O 控制。它是 Unix 系统调用 fcntl()ioctl() 的接口。关于这些调用的完整描述,请参阅 Unix 手册的 fcntl(2)ioctl(2) 页面。

flock介绍

fcntl.flock(f, operation)

f: 文件描述符
operation:  操作
    fcntl.LOCK_UN  解锁
    fcntl.LOCK_EX  排他锁:其他进程会阻塞等待
    fcntl.LOCK_SH  共享锁:所有进程没有写访问权限,即使是加锁进程也没有。所有进程有读访问权限。
    fcntl.LOCK_NB  非阻塞锁:如果获取不到锁,将引发OSError异常

flock用法

flock锁可以递归,即通过dup或者fork产生的两个fd,都可以进行加锁而不会死锁。

  • 对文件 close 后文件锁会失效
  • 进程结束后文件锁会失效
import fcntl

# flock 生成的是劝告锁,因此进程可以正常打开文件
fd = open("/tmp/test.txt")
# 检测文件是否被加锁。如果已经上了锁,那么这里就会被阻塞
fcntl.flock(fd, fcntl.LOCK_EX)
# fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)  # 用 LOCK_NB 就不会被阻塞

# 解锁
# fd.close()也会解锁
fcntl.flock(fd, fcntl.LOCK_UN)  # 对文件进行解锁

配合with使用

with后会自动关闭文件,相当于自动解锁了

import fcntl
file_name = "/tmp/test.txt"
with open(file_name , 'r') as f:
  # 添加排他锁
  fcntl.flock(f, fcntl.LOCK_EX)
  # To do something

类方法装饰器使用

def lock_file(file_name="lock.txt"):
  def wrapper(func):
    def inner(obj, *args, **kwargs):
      branch = obj.env.to_branch
      lock_file_path = os.path.join(os.environ["HOME"], "lock", branch, file_name)
      if not os.path.exists(lock_file_path):
        make_dir(os.path.dirname(lock_file_path))
        run("touch " + lock_file_path)
      info(file_name.center(60, '='))
      with open(lock_file_path, 'r+') as f:
        try:
          fcntl.flock(f, fcntl.LOCK_NB)
          info(f'run func: <self.{func.__name__}>...')
          res = func(obj, *args, **kwargs)
          return res
        except IOError:
          fatal(f"另一个进程正在操作改目录[{branch}]!!!")
    return inner
  return wrapper

flock和lockf的区别

第一个区别是flock只能对整个文件进行上锁,而不能对文件的某一部分上锁,lockf可以对文件的某个区域进行上锁。
第二个区别是flock只能产生劝告性锁。flock可以有共享锁和排他锁,而lockf只支持排他锁。
第三个区别主要是在使用fork/dup的情况。
第四个区别是flock不能在NFS文件系统上使用,要在NFS上使用需要用 fcntl。