web-dev-qa-db-ja.com

Python fcntlが期待どおりにロックしない

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")
24
Ingo Fischer

とった。スクリプトのエラーは、呼び出しごとに新しいファイル記述子を作成することです。

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を使用することがどのような場合に意味があるかについて考えなければなりません。

10
Ingo Fischer

古い投稿ですが、他の誰かがそれを見つけた場合、私はこの動作をします:

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

最初のケースのように見えますが、おそらくファイルオブジェクトにアクセスできないため、ファイルは最初の行の後で閉じられます。ファイルを閉じるとロックが解除されます。

15
philh

私は同じ問題を抱えていました...開いたファイルを別の変数に保持して解決しました:

動作しません:

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
hgdeoro

キャッチは2つあります。 ドキュメント によると:

  1. 操作がLOCK_SHまたはLOCK_EXの場合、LOCK_NBとビット単位のORをとることで、ロック取得時のブロックを回避できます。 LOCK_NBが使用されていてロックを取得できない場合は、IOErrorが発生し、例外のerrno属性がEACCESまたはEAGAIN(オペレーティングシステムによって異なります。移植性については、両方の値を確認してください)。

    LOCK_NBの設定を忘れました。

  2. 少なくとも一部のシステムでは、LOCK_EXは、ファイル記述子が書き込み用に開かれたファイルを参照している場合にのみ使用できます。

    ファイルを読み取り用に開いていますが、システムでLOCK_EXをサポートしていない可能性があります。

6
orlp

さまざまなロックインスキームの詳細については、この post を参照してください。
2番目の質問については、fcntlを使用して異なるプロセス間でロックを取得します(簡単にするためにlockfを使用してください)。 Linuxでは、lockffcntlのラッパーにすぎず、どちらも(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')
1
lyu.l

ファイル記述子を渡す必要があります(ファイルオブジェクトのfileno()メソッドを呼び出すことで取得できます)。以下のコードは、同じコードが別のインタープリターで実行されるとIOErrorをスローします。

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

試してください:

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

ファイルが閉じられると、ロックは消えます。

0
rao xp