Python fcntl не блокируется, как ожидалось - PullRequest
22 голосов
/ 28 марта 2012

В ОС на основе 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")

Но в примере просто выводится «Нет ошибки».

Если я разделю этот код на два клиента, работающих одновременно (один блокируется, а затем ожидает, другой пытается заблокировать после того, как первая блокировка уже активна), я получаю то же самое поведение - никакого эффекта привсе.

Чем объясняется такое поведение?

РЕДАКТИРОВАТЬ :

Изменения в соответствии с запросом 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")

Ответы [ 7 ]

14 голосов
/ 29 июня 2013

У меня та же проблема ... Я решил ее, держа открытый файл в отдельной переменной:

Не будет работать:

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)

Я думаю, что первый не 'работает, потому что открытый файл сборщик мусора , закрыт и блокировка снята .

14 голосов
/ 10 апреля 2013

Старый пост, но если кто-то еще найдет его, я получаю следующее поведение:

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

Похоже, что в первом случае файл закрывается после первой строки, предположительно потому, что объект файла недоступен. Закрытие файла снимает блокировку.

9 голосов
/ 28 марта 2012

Понял. Ошибка в моем сценарии заключается в том, что я создаю новый файловый дескриптор при каждом вызове:

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.

6 голосов
/ 28 марта 2012

Есть два улова.В соответствии с документацией :

  1. Когда операция имеет значение LOCK_SH или LOCK_EX, ее также можно поразрядно ORed с LOCK_NB, чтобы избежать блокировкина приобретение замка.Если используется LOCK_NB, и блокировка не может быть получена, будет сгенерировано IOError, а для исключения для атрибута errno будет установлено значение EACCES или EAGAIN (в зависимости от операционной системы; для переносимости проверьтедля обоих значений).

    Вы забыли установить LOCK_NB.

  2. По крайней мере, в некоторых системах LOCK_EX можно использовать толькоесли дескриптор файла ссылается на файл, открытый для записи.

    У вас есть файл, открытый для чтения, который может не поддерживать LOCK_EX в вашей системе.

0 голосов
/ 08 октября 2018

вы можете обратиться к этому сообщению для получения более подробной информации о различных схемах блокировки.
Что касается вашего второго вопроса, используйте 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')
0 голосов
/ 14 августа 2014

Попробуйте:

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

Когда файл будет закрыт, блокировка исчезнет.

0 голосов
/ 28 марта 2012

Вам необходимо передать дескриптор файла (можно получить, вызвав метод fileno () объекта file).Код ниже выдает IOError, когда тот же код выполняется в отдельном интерпретаторе.

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