在基于Debian的操作系统(Ubuntu,Debian Squeeze)上,我使用Python(2.7,3.2)fcntl来锁定文件。据我所知,fnctl.flock以某种方式锁定文件,如果另一个客户端想要锁定同一个文件,则会抛出异常。
我构建了一个小例子,我希望抛出一个例外,因为我先锁定文件,然后,我立即尝试再次锁定它:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import fcntl
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX)
try:
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
print("can't immediately write-lock the file ($!), blocking ...")
else:
print("No error")
但是这个例子只是打印“没有错误”。
如果我将这段代码分成两个同时运行的客户端(一个锁定然后等待,另一个在第一个锁已经激活后尝试锁定),我会得到相同的行为 - 根本没有效果。< / p>
这种行为的解释是什么?
修改:
按照nightcracker的要求进行更改,此版本还会打印“无错误”,但我不希望这样:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import fcntl
import time
fcntl.flock(open('/tmp/locktest', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
try:
fcntl.flock(open('/tmp/locktest', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
print("can't immediately write-lock the file ($!), blocking ...")
else:
print("No error")
答案 0 :(得分:14)
旧帖子,但如果有其他人发现它,我会得到这种行为:
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX)
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
# That didn't throw an exception
>>> f = open('test.flock', 'w')
>>> fcntl.flock(f, fcntl.LOCK_EX)
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IOError: [Errno 35] Resource temporarily unavailable
>>> f.close()
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
# No exception
看起来在第一种情况下,文件在第一行之后关闭,可能是因为文件对象不可访问。关闭文件会释放锁定。
答案 1 :(得分:13)
我讨厌同样的问题......我已经解决了将打开的文件保存在一个单独的变量中的问题:
无效:
fcntl.lockf(open('/tmp/locktest', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
使用:
lockfile = open('/tmp/locktest', 'w')
fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
我认为第一个不起作用是因为打开的文件垃圾收集,关闭并且锁定已发布。
答案 2 :(得分:9)
知道了。我的脚本中的错误是我在每次调用时创建一个新的文件描述符:
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)
(...)
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)
相反,我必须将文件对象分配给变量,而不是尝试锁定:
f = open('/tmp/locktest', 'r')
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
(...)
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
我也得到了我想看到的例外:IOError: [Errno 11] Resource temporarily unavailable
。现在我必须考虑在哪些情况下使用fcntl是有意义的。
答案 3 :(得分:5)
有两次捕获。根据{{3}}:
当操作为
LOCK_SH
或LOCK_EX
时,它也可以与LOCK_NB
进行按位或运算,以避免阻塞锁定获取。如果使用LOCK_NB
并且无法获取锁定,则会引发IOError
,并且例外errno
属性设置为EACCES
或EAGAIN
(取决于操作系统;为了便于携带,请检查两个值。)
您忘了设置LOCK_NB
。
至少在某些系统上,
LOCK_EX
只能在文件描述符引用为写入而打开的文件时使用。
您已打开一个文件供阅读,但可能不支持您系统上的LOCK_EX
。
答案 4 :(得分:0)
您需要传入文件描述符(可通过调用文件对象的fileno()方法获得)。当在单独的解释器中运行相同的代码时,下面的代码会引发IOError。
>>> import fcntl
>>> thefile = open('/tmp/testfile')
>>> fd = thefile.fileno()
>>> fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
答案 5 :(得分:0)
尝试:
global f
f = open('/tmp/locktest', 'r')
当文件关闭时,锁定将消失。
答案 6 :(得分:0)
您可以参考此post,了解有关不同锁定方案的更多详细信息。
至于第二个问题,请使用fcntl
来锁定不同的进程(为简单起见,请使用lockf
)。在Linux上,lockf
只是fcntl
的包装,两者都与(pid, inode)
对关联。
1.使用fcntl.fcntl
跨进程提供文件锁定。
import os
import sys
import time
import fcntl
import struct
fd = open('/etc/mtab', 'r')
ppid = os.getpid()
print('parent pid: %d' % ppid)
lockdata = struct.pack('hhllh', fcntl.F_RDLCK, 0, 0, 0, ppid)
res = fcntl.fcntl(fd.fileno(), fcntl.F_SETLK, lockdata)
print('put read lock in parent process: %s' % str(struct.unpack('hhllh', res)))
if os.fork():
os.wait()
lockdata = struct.pack('hhllh', fcntl.F_UNLCK, 0, 0, 0, ppid)
res = fcntl.fcntl(fd.fileno(), fcntl.F_SETLK, lockdata)
print('release lock: %s' % str(struct.unpack('hhllh', res)))
else:
cpid = os.getpid()
print('child pid: %d' % cpid)
lockdata = struct.pack('hhllh', fcntl.F_WRLCK, 0, 0, 0, cpid)
try:
fcntl.fcntl(fd.fileno(), fcntl.F_SETLK, lockdata)
except OSError:
res = fcntl.fcntl(fd.fileno(), fcntl.F_GETLK, lockdata)
print('fail to get lock: %s' % str(struct.unpack('hhllh', res)))
else:
print('succeeded in getting lock')
2。使用fcntl.lockf
。
import os
import time
import fcntl
fd = open('/etc/mtab', 'w')
fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
if os.fork():
os.wait()
fcntl.lockf(fd, fcntl.LOCK_UN)
else:
try:
fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError as e:
print('failed to get lock')
else:
print('succeeded in getting lock')