2012-03-28 13 views
17

W systemie operacyjnym opartym na Debianie (Ubuntu, Squeeze Debiana) używam Pythona (2.7, 3.2) fcntl, aby zablokować plik. Jak rozumiem z tego, co przeczytałem, fnctl.flock blokuje plik w taki sposób, że wyjątek zostanie zgłoszony, jeśli inny klient chce zablokować ten sam plik.Python fcntl nie blokuje zgodnie z oczekiwaniami

Zbudowałem mały przykład, który spodziewałbym się rzucić excepiton, odkąd pierwszy zablokować plik, a następnie, bezpośrednio po, staram się zablokować go ponownie:

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

ale przykład tylko drukuje "Brak błędu".

Jeśli podzielę ten kod maksymalnie na dwa klienty działające w tym samym czasie (jedno blokowanie, a następnie czekanie, a drugi próbuje zablokować po tym, jak pierwsza blokada jest już aktywna), otrzymam to samo zachowanie - brak efektu.

Jakie jest wytłumaczenie tego zachowania?

EDIT:

Zmiany wymagane przez nightcracker, ta wersja drukuje także "Brak błędu", chociaż nie spodziewałbym się, że:

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

Istnieje [haczyk dotyczący blokad plików w ramach tego samego procesu] (http://0pointer.de/blog/projects/locking.html). W ramach jednego procesu wątki będą dzielić blokadę pliku. – bouke

Odpowiedz

7

Rozumiem. Błąd w moim scenariuszu jest to, że tworzę nowy deskryptor pliku z każdej rozmowy:

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

Zamiast tego muszę przypisać obiekt pliku do zmiennej i nie próbować zablokować:

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

Niż Dostaję również wyjątek, który chciałem zobaczyć: IOError: [Errno 11] Resource temporarily unavailable.Teraz muszę się zastanowić, w których przypadkach w ogóle ma sens używanie fcntl.

+2

Otrzymuję komunikat "Brak błędu" :(. Czy wiesz dlaczego? – Etam

+16

Aby było jasne, błąd nie polega na tym, że tworzysz * nowy * deskryptor pliku przy każdym wywołaniu, ale że poprzedni * deskryptor pliku został śmieci zostały zebrane (i poprzednia blokada idzie z nim) .Jeśli miałbyś zapisać oba te deskryptory plików do różnych zmiennych, skrypt zadziałałby –

+0

Ta odpowiedź jest myląca. Komentarz powyżej @Dustin jest poprawny –

3

Istnieją dwie połowy. Według documentation:

  1. Kiedy operacja jest LOCK_SH lub LOCK_EX, może być również logicznie ORed z LOCK_NB aby uniknąć blokowania na nabycie blokady. Jeśli zostanie użyte LOCK_NB i nie można uzyskać blokady, zostanie podniesiony IOError, a wyjątek będzie miał atrybut errno ustawiony na EACCES lub EAGAIN (w zależności od systemu operacyjnego, dla przenośności sprawdź obie wartości).

    Zapomniałeś ustawić LOCK_NB.

  2. przynajmniej na niektórych systemach LOCK_EX mogą być stosowane tylko wtedy, gdy deskryptor pliku odnosi się plik otwarty do zapisu.

    masz plik otwarty do odczytu, co może nie obsługiwać LOCK_EX w systemie.

+0

dzięki. Ale obie zmiany (dodanie fcntl.LOCK_NB do pierwszej blokady ORAZ otwarcie pliku do zapisania) nie zmieniają zachowania, wciąż otrzymuję komunikat "Brak błędu". – ifischer

+1

@ifischer: czy 'fcntl' działa z C w twoim systemie? Jakiej wersji Python używasz? – orlp

+0

Wypróbowałem zarówno Python 2.7.1, jak i Python 3.2. Spróbuję C później – ifischer

0

Musisz przekazać deskryptor pliku (możliwy do uzyskania przez wywołanie metody fileno() obiektu pliku). Poniższy kod zgłasza IOError, gdy ten sam kod jest uruchamiany w oddzielnym interpretera.

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

To nie powinno być konieczne. Z [documentation] (http://docs.python.org/library/fcntl.html#fcntl.flock): _Projektuj operację blokady na deskryptorze pliku fd (__file obiekty udostępniające metodę fileno() również są akceptowane__) ._ – orlp

+0

zastosowanie tego nie zmienia zachowania, wciąż otrzymuję komunikat "Brak błędu", w przeciwieństwie do tego, czego oczekiwałbym – ifischer

+0

@ifischer: Nieparzysty, wkleiłem powyższy kod do dwóch interpreterów Pythona na komputerze Ubuntu, a pierwszy został ukończony, drugi rzucił wyjątek. – Vatine

10

Stary post, ale jeśli ktoś go znajdzie, mam ten problem:

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

To wygląda jak w pierwszym przypadku, plik zostanie zamknięty po pierwszej linii, przypuszczalnie dlatego, że obiekt pliku jest niedostępne . Zamknięcie pliku powoduje zwolnienie blokady.

10

ja hade ten sam problem ... I został rozwiązany to trzymając otwarty plik w osobnej zmiennej:

nie będzie działać:

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) 

Myślę, że pierwszy nie działa, ponieważ otwarty plik to śmieci zebrane, zamknięte i zamek zwolniony.

+3

Pamiętaj, że używasz 'lockf' podczas gdy OP użył' flock' w oryginalnym poście.Te dwie są bardzo różne implementacje! Złe nazwy, trudne do złapania;) –

0

Spróbuj:

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

Gdy plik jest zamknięty blokada zniknie.

+0

Jeśli jest to dwa różne procesy, to jest absolutnie bezużyteczne. Powinien używać fcntl.flock – theBuzzyCoder

Powiązane problemy