如何删除没有竞争条件的锁定(flock)文件:在释放锁之前或之后?

时间:2015-10-16 09:05:51

标签: python ipc delete-file flock

我正在实现基于文件锁的互斥机制。我的脚本的其他实例知道当它们来到特定文件时它们不应该运行,该文件被锁定。

为了实现这一目标,我使用fcntl.flock创建并锁定了文件。当我释放锁时,我还想清理文件,当没有进程实际运行时,它不会坐在那里指示一个旧的pid。

我的问题是,何时以及如何清理文件,尤其是在什么时候我可以删除它。基本上我看到两个选择:

  • 在发布锁定之前截断并删除文件
  • 在锁定释放后截断并删除文件

根据我的理解,每个人都会将我的应用暴露给略有不同的竞争条件。什么是最佳实践,我错过了什么?

这是一个(过于简化)示例:

import fcntl
import os
import sys
import time

# open file for read/write, create if necessary
with open('my_lock_file.pid', 'a+') as f:
    # acquire lock, raises exception if lock is hold by another process
    try:
        fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError:
        print 'other process running:', f.readline()
        sys.exit()

    try:
        # do something
        f.write('%d\n' % os.getpid())
        f.flush()
        # more stuff here ...
        time.sleep(5)
    finally:
        # clean up here?
        # release lock
        fcntl.flock(f, fcntl.LOCK_UN)
        # clean up here?
# clean up here?

2 个答案:

答案 0 :(得分:1)

我发现这个相关的问题,就如何处理这种情况提出一些建议:

它还让我意识到另一种可能的竞争条件,这种情况发生在另一个进程在当前进程打开文件之后删除文件时。 这将导致当前进程锁定不再在文件系统上找到的文件,从而无法阻止将重新创建它的下一个进程。

在那里我发现了使用open标志O_EXCL来创建原子独占文件的建议,它通过os.open()函数公开,用于低级文件操作。 然后,我相应地实现了以下示例:

import os
import sys
import time

# acquire: open file for write, create if possible, exclusive access guaranteed
fname = 'my_lock_file.pid'
try:
    fd = os.open(fname, os.O_CREAT|os.O_WRONLY|os.O_EXCL)
except OSError:
    # failed to open, another process is running
    with open(fname) as f:
        print "other process running:", f.readline()
        sys.exit()

try:
    os.write(fd, '%d\n' % os.getpid())
    os.fsync(fd)
    # do something
    time.sleep(5)
finally:
    os.close(fd)
    # release: delete file
    os.remove(fname)

执行此操作后,我发现这与lockfile模块用于其pid文件的方法完全相同。

供参考:

答案 1 :(得分:0)

  1. 在Unix中,可以在打开文件时将其删除-直到所有进程都结束了ino为止,该ino将保留在文件描述符列表中
  2. 在Unix中,可以通过检查链接计数为零来检查文件是否已从所有目录中删除

每次创建锁文件时,在Unix下这可能是有效的解决方案:

import fcntl
import os
import sys
import time

# open file for read/write, create if necessary
for attempt in xrange(timeout):
    with open('my_lock_file.pid', 'a+') as f:
        # acquire lock, raises exception if lock is hold by another process
        try:
            fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
            st = os.fstat(f)
            if not st.st_nlink:
                print 'lock file got deleted'
                continue # try again
        except BlockingIOError:
            print 'other process running:', f.readline()
            time.sleep(1)
            continue # try again

        try:
            # do something
            f.write('%d\n' % os.getpid())
            f.flush()
            # more stuff here ...
            time.sleep(5)
        finally:
            # clean up here
            os.unlink('my_lock_file.pid')
            # release lock
            fcntl.flock(f, fcntl.LOCK_UN)
    return True
return False

家庭作业:解释为什么将unix的传统文件名命名为“ unlink”。