父进程退出后flock锁提前释放问题及解决方法咨询
问题:flock锁在父进程退出后提前释放,如何保持到父子进程都关闭文件描述符?
我先获取flock锁再执行fork创建子进程,期望锁仅在父子进程均退出后才解锁。但父进程结束后触发断言:
test.lck NOT found in lslocks output
Traceback (most recent call last):
File "daemon_and_flock.py", line 25, in
assert check_lock(path)
AssertionError
相关代码如下:
import fcntl, sys, os, subprocess, time def check_lock(path: str) -> bool: lines = subprocess.check_output(["lslocks"]).decode('utf-8') for line in lines.split('\n'): if path in line: print(f"{path} found in lslocks output") return True print(f"{path} NOT found in lslocks output") return False path = sys.argv[1] fh = open(path, "a") fcntl.flock(fh, fcntl.LOCK_EX) assert check_lock(path) print(f"before fork: file handle {fh.fileno()}") pid = os.fork() if pid == 0: print(f"child: file handle {fh.fileno()}") assert check_lock(path) time.sleep(1) assert check_lock(path) else: print(f"parent: file handle {fh.fileno()}") assert check_lock(path)
原因分析
flock是进程级锁,内核会在进程退出时自动释放该进程持有的所有flock锁。父进程fork后直接退出,其持有的锁被立即释放,而子进程虽继承了文件描述符,但无法继承父进程的flock锁持有状态,导致子进程的断言失败。
解决方案
要实现“锁保持到所有相关文件描述符关闭”的需求,有两种可行方案:
方案一:改用fcntl文件描述符级记录锁
fcntl的记录锁与文件描述符绑定,只要有一个关联的文件描述符未关闭,锁就会保持。修改后的代码如下:
import fcntl, sys, os, subprocess, time, struct def check_lock(path: str) -> bool: lines = subprocess.check_output(["lslocks"]).decode('utf-8') for line in lines.split('\n'): if path in line: print(f"{path} found in lslocks output") return True print(f"{path} NOT found in lslocks output") return False path = sys.argv[1] fh = open(path, "a") # 替换flock为fcntl写锁(覆盖整个文件) lock_struct = struct.pack('hhllhh', fcntl.F_WRLCK, 0, 0, 0, 0, 0) fcntl.fcntl(fh, fcntl.F_SETLK, lock_struct) assert check_lock(path) print(f"before fork: file handle {fh.fileno()}") pid = os.fork() if pid == 0: print(f"child: file handle {fh.fileno()}") assert check_lock(path) time.sleep(1) assert check_lock(path) fh.close() # 子进程退出前关闭文件描述符 else: print(f"parent: file handle {fh.fileno()}") assert check_lock(path) fh.close() # 父进程可提前关闭,锁由子进程保持 # 可选:若需等待子进程,添加 os.waitpid(pid, 0)
方案二:父进程等待子进程退出(保留flock)
如果必须使用flock,可让父进程阻塞等待子进程结束,确保父进程的锁一直持有到子进程退出:
# 修改原代码的父进程分支 else: print(f"parent: file handle {fh.fileno()}") assert check_lock(path) os.waitpid(pid, 0) # 等待子进程退出 assert check_lock(path)
此方案下父进程无法提前退出,需等待子进程结束后才会释放锁。
内容的提问来源于stack exchange,提问作者user1244932
相关产品推荐
相关产品推荐

