fcntl文件枷锁模块
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
类方法装饰器使用
import os
import fcntl
import subprocess
def lock_file(file_name="lock.txt"):
def wrapper(func):
def inner(obj, *args, **kwargs):
lock_file_path = os.path.join(os.environ["HOME"], "lock", file_name)
if not os.path.exists(lock_file_path):
subprocess.run("mkdir -p " + os.path.dirname(lock_file_path))
subprocess.run("touch " + lock_file_path)
print(file_name.center(60, '='))
with open(lock_file_path, 'r+') as f:
try:
# 非阻塞锁
fcntl.flock(f, fcntl.LOCK_NB)
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。