关于fcntl锁删除行为及单实例脚本锁实现的技术咨询
关于fcntl文件锁的释放行为详解
嘿,针对你用fcntl实现脚本单实例运行的场景,我来给你详细梳理下这些锁的释放/删除逻辑:
1. 正常场景:文件描述符关闭即自动释放
这是最核心的规则:fcntl锁是和文件描述符绑定的,不是和锁文件本身绑定的。
- 不管你是显式调用
fd.close(),还是脚本正常执行完毕(Python会自动帮你清理所有打开的文件描述符),只要锁对应的文件描述符被关闭,内核就会立刻释放这个锁。 - 举个例子:哪怕你把
mylockfile这个文件删了,只要持有锁的进程还没关闭那个文件描述符,锁依然有效;反过来,只要文件描述符关了,哪怕锁文件还留在磁盘上,锁也会被释放。
2. 异常场景:进程终止后自动释放
如果你的脚本因为崩溃、被kill(哪怕是强制终止的SIGKILL信号)等异常情况退出,内核会自动回收该进程持有的所有文件描述符,对应的fcntl锁也会被自动释放。
- 这也是fcntl锁比那种“创建标记文件判断运行状态”方案更靠谱的地方——不会因为进程异常挂掉留下没人管的“僵尸锁”。
3. 手动释放锁的方式
如果需要在脚本运行中途提前释放锁,可以调用lockf的解锁指令:
lockf(fd, LOCK_UN)
不过在你的单实例场景里,一般不需要手动做这个,等脚本结束自动释放就足够了。
给你代码的小优化建议
看了你写的代码,有两个小细节可以调整得更健壮:
- 用
with语句管理文件描述符,这样不管脚本是正常结束还是异常退出,都会自动关闭文件,避免意外的锁残留:import traceback from errno import EACCES, EAGAIN from fcntl import lockf, LOCK_EX, LOCK_NB, LOCK_UN from time import sleep import os if __name__ == '__main__': with open("mylockfile", 'w') as fd: fd.write("%d " % os.getpid()) try: lockf(fd, LOCK_EX | LOCK_NB) print("Obtained lock") # 这里做你的业务逻辑 sleep(10) except OSError as e: if e.errno in (EACCES, EAGAIN): print("Script already running") print(e.errno) # 打印完整异常栈 traceback.print_exc() - 你代码里的
traceback....应该补全为traceback.print_exc(),这样才能正确打印异常的堆栈信息。
常见误区澄清
- 误区1:删除锁文件就能释放锁?完全不对!锁是内核维护的状态,和磁盘上的锁文件是否存在无关。如果删除了锁文件,持有锁的进程依然持有锁,而新进程打开同名文件时会创建新的inode,这时候新进程能获取到锁,直接导致你的单实例逻辑失效。所以千万别随便删锁文件。
- 误区2:锁文件里写的PID能控制锁?其实这个PID只是用来排查哪个进程在持有锁,内核根本不会根据这个PID来管理锁的释放——哪怕你手动修改锁文件里的PID,锁依然会牢牢握在原进程手里。
内容的提问来源于stack exchange,提问作者Deepthi Nidwannaya
相关产品推荐
相关产品推荐

