DebianベースのOS(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")
ただし、この例では「エラーなし」と表示されます。
このコードを同時に実行している2つのクライアントに分割した場合(1つはロックしてから待機し、もう1つは最初のロックがすでにアクティブになった後にロックしようとしています)、同じ動作が得られます-まったく影響がありません。
この動作の説明は何ですか?
[〜#〜]編集[〜#〜]:
ナイトクラッカーからの要求に応じて、このバージョンでは「エラーなし」が出力されますが、私はそうは思わないでしょう。
#!/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")
とった。スクリプトのエラーは、呼び出しごとに新しいファイル記述子を作成することです。
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を使用することがどのような場合に意味があるかについて考えなければなりません。
古い投稿ですが、他の誰かがそれを見つけた場合、私はこの動作をします:
>>> 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
最初のケースのように見えますが、おそらくファイルオブジェクトにアクセスできないため、ファイルは最初の行の後で閉じられます。ファイルを閉じるとロックが解除されます。
私は同じ問題を抱えていました...開いたファイルを別の変数に保持して解決しました:
動作しません:
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つあります。 ドキュメント によると:
操作が
LOCK_SH
またはLOCK_EX
の場合、LOCK_NB
とビット単位のORをとることで、ロック取得時のブロックを回避できます。LOCK_NB
が使用されていてロックを取得できない場合は、IOError
が発生し、例外のerrno
属性がEACCES
またはEAGAIN
(オペレーティングシステムによって異なります。移植性については、両方の値を確認してください)。
LOCK_NB
の設定を忘れました。
少なくとも一部のシステムでは、
LOCK_EX
は、ファイル記述子が書き込み用に開かれたファイルを参照している場合にのみ使用できます。
ファイルを読み取り用に開いていますが、システムでLOCK_EX
をサポートしていない可能性があります。
さまざまなロックインスキームの詳細については、この post を参照してください。
2番目の質問については、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')
ファイル記述子を渡す必要があります(ファイルオブジェクトのfileno()メソッドを呼び出すことで取得できます)。以下のコードは、同じコードが別のインタープリターで実行されるとIOErrorをスローします。
>>> import fcntl
>>> thefile = open('/tmp/testfile')
>>> fd = thefile.fileno()
>>> fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
試してください:
global f
f = open('/tmp/locktest', 'r')
ファイルが閉じられると、ロックは消えます。