使用flock、fork和终止父进程时出现死锁情况

7

我有一个相当复杂的Python程序。它内部有一个日志记录系统,使用一个独占(LOCK_EXfcntl.flock来管理全局锁定。实际上,每当一个日志消息被转储时,都会获得全局文件锁,将消息发送到文件(不同于锁定文件),然后释放全局文件锁。

该程序在设置日志管理后也会多次进行自我复制。通常一切正常。

如果父进程被kill掉(而子进程保持存活状态),偶尔会出现死锁。所有程序都永远地阻塞在 fcntl.flock()上。尝试外部获取锁定同样永远地阻塞。我必须kill掉子进程才能解决这个问题。

但令人困惑的是,lsof lock_file并没有显示任何持有锁的进程!因此,我无法弄清楚为什么文件被内核锁定,但没有进程报告它正在持有锁定。

flock在分叉方面是否存在问题?即使已不在进程表中,死亡的父级是否仍在持有锁定?如何解决这个问题?


1
好的,我切换到了fcntl.lockf,它包装了fcntl锁(而不是flock)。死锁问题解决了。 - UsAaR33
我怀疑这是因为flock锁定了文件描述符(在子进程中仍然存在),而fcntl使用inode / pid进行锁定。不过奇怪的是,lsof没有解决子进程实际上拥有flock的问题;这是为什么呢? - UsAaR33
1个回答

4

lsof几乎肯定不会显示flock()锁,因此不看到这个锁并不能告诉你是否存在。

flock()锁是通过fd共享(dup()系统调用或保持文件打开的fork-and-exec)继承的,任何具有共享描述符的人都可以解锁锁定,但如果锁已经被占用,则任何尝试再次锁定它的尝试都将被阻塞。 因此,可能是父进程锁定了描述符,然后死亡,使描述符保持锁定状态。 然后子进程也尝试锁定并被阻塞,因为描述符已经被锁定。(如果子进程锁定了文件,然后死亡,情况将是相同的。)

由于`fcntl()`锁是每个进程的,所以正在退出的进程释放其所有锁,以便你可以继续进行,这正是你在这里想要的。


谢谢@torek。你提到:“很可能是父进程锁定了描述符,然后死亡,导致描述符被锁定。”我的问题是,当父进程死亡时,fd不是会自动释放吗? - kgf3JfUtW
1
@sam: 当一个进程停止运行时,它打开的文件会被关闭,某些锁会被释放,而某些则不会。flock() 的那些锁除非这是文件的最后一个关闭操作否则不会被释放。即使这不是最后一个关闭操作, fcntl(fd, F_SETLK, ...) 的那些锁也会被释放。这里的“最后”是通过文件描述符共享来确定的,使用dup(从而在基于vnode的操作系统中,有第二种“最后关闭”,用于设备关闭和vnode回收)。请查看您正在使用的特定类型锁的文档。 - torek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接