简体   繁体   English

Python fcntl没有按预期锁定

[英]Python fcntl does not lock as expected

On a Debian-based OS (Ubuntu, Debian Squeeze), I'm using Python (2.7, 3.2) fcntl to lock a file. 在基于Debian的操作系统(Ubuntu,Debian Squeeze)上,我使用Python(2.7,3.2)fcntl来锁定文件。 As I understand from what I read, fnctl.flock locks a file in a way, that an exception will be thrown if another client wants to lock the same file. 据我所知,fnctl.flock以某种方式锁定文件,如果另一个客户端想要锁定同一个文件,则会抛出异常。

I built a little example, which I would expect to throw an excepiton, since I first lock the file, and then, immediately after, I try to lock it again: 我构建了一个小例子,我希望抛出一个例外,因为我先锁定文件,然后,我立即尝试再次锁定它:

#!/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")

But the example just prints "No error". 但该示例只是打印“无错误”。

If I split this code up to two clients running at the same time (one locking and then waiting, the other trying to lock after the first lock is already active), I get the same behavior - no effect at all. 如果我将此代码分割为两个同时运行的客户端(一个锁定然后等待,另一个在第一个锁已经激活后尝试锁定),我得到相同的行为 - 完全没有效果。

Whats the explanation for this behavior? 对这种行为的解释是什么?

EDIT : 编辑

Changes as requested by nightcracker, this version also prints "No error", although I would not expect that: 根据nightcracker的要求进行更改,此版本还会打印“No error”,尽管我不希望这样:

#!/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")

Old post, but if anyone else finds it, I get this behaviour: 旧帖子,但如果有人发现它,我会得到这样的行为:

>>> 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

It looks like in the first case, the file is closed after the first line, presumably because the file object is inaccessible. 看起来在第一种情况下,文件在第一行之后关闭,可能是因为文件对象不可访问。 Closing the file releases the lock. 关闭文件会释放锁定。

I hade the same problem... I've solved it holding the opened file in a separate variable: 我讨厌同样的问题...我已经解决了将打开的文件保存在一个单独的变量中:

Won't work: 不会工作:

fcntl.lockf(open('/tmp/locktest', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)

Works: 作品:

lockfile = open('/tmp/locktest', 'w')
fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)

I think that the first doesnt' works because the opened file is garbage collected , closed and the lock released . 我认为第一个不起作用,因为打开的文件是垃圾收集关闭锁定释放

Got it. 得到它了。 The error in my script is that I create a new file descriptor on each call: 我的脚本中的错误是我在每次调用时创建一个新的文件描述符:

fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)
(...)
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)

Instead, I have to assign the file object to a variable and than try to lock: 相反,我必须将文件对象分配给变量,而不是尝试锁定:

f = open('/tmp/locktest', 'r')
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
(...)
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)

Than I'm also getting the exception I wanted to see: IOError: [Errno 11] Resource temporarily unavailable . 我也得到了我想看到的异常: IOError: [Errno 11] Resource temporarily unavailable Now I have to think about in which cases it makes sense at all to use fcntl. 现在我必须考虑在哪些情况下使用fcntl是有意义的。

There are two catches. 有两个渔获量。 According to the documentation : 根据文件

  1. When operation is LOCK_SH or LOCK_EX , it can also be bitwise ORed with LOCK_NB to avoid blocking on lock acquisition. 当操作是LOCK_SHLOCK_EX ,它也可以与LOCK_NB进行按位OR运算,以避免阻塞锁定。 If LOCK_NB is used and the lock cannot be acquired, an IOError will be raised and the exception will have an errno attribute set to EACCES or EAGAIN (depending on the operating system; for portability, check for both values). 如果使用LOCK_NB并且无法获取锁定,则会IOError ,并且异常将errno属性设置为EACCESEAGAIN (取决于操作系统;为了便携性,请检查两个值)。

    You forgot to set LOCK_NB . 你忘了设置LOCK_NB

  2. On at least some systems, LOCK_EX can only be used if the file descriptor refers to a file opened for writing. 至少在某些系统上,只有在文件描述符引用为写入而打开的文件时才能使用LOCK_EX

    You have a file opened for reading, which might not support LOCK_EX on your system. 您打开了一个用于阅读的文件,该文件可能不支持您系统上的LOCK_EX

Try: 尝试:

global f
f = open('/tmp/locktest', 'r')

When the file is closed the lock will vanish. 当文件关闭时,锁定将消失。

you could refer to this post for more details of different lockin schemes. 你可以参考这篇文章了解不同锁定方案的更多细节。
As for your second question, use fcntl to get lock across different process(use lockf instead for simplicity). 至于你的第二个问题,使用fcntl来锁定不同的进程(为简单起见,使用lockf )。 On linux lockf is just a wrapper for fcntl , both are associated with (pid, inode) pair. 在linux上, lockf只是fcntl的包装器,两者都与(pid, inode)对相关联。
1. use fcntl.fcntl to provide file lock across processes. 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. use fcntl.lockf . 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')

You need to pass in the file descriptor (obtainable by calling the fileno() method of the file object). 您需要传入文件描述符(可通过调用文件对象的fileno()方法获得)。 The code below throws an IOError when the same code is run in a separate interpreter. 当在单独的解释器中运行相同的代码时,下面的代码会引发IOError。

>>> import fcntl
>>> thefile = open('/tmp/testfile')
>>> fd = thefile.fileno()
>>> fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM