web-dev-qa-db-ja.com

Flockが機能していないようです

最近、マイナープロジェクト用のシェルスクリプトを作成しようとしていますが、何らかの理由でflockコマンドが適切に機能しません。サブシェルでアトミックな方法で呼び出してバックグラウンドに置くと、他のプログラムはロックされたファイルの読み取り/書き込みを行うことができるようです。

バッシュセッション:

guest@guest ~ $ touch ./temp
guest@guest ~ $ ( flock -x 3 && sleep 99999999999; ) 3>./temp &
[1] 22874
guest@guest ~ $ cat ./temp 
guest@guest ~ $ echo this is a test >./temp 
guest@guest ~ $ cat ./temp 
this is a test
guest@guest ~ $ jobs
[1]+  Running                 ( flock -x 3 && sleep 99999999999 ) 3>./temp &
guest@guest ~ $ bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

「群れ」プログラムの内部に関連する非常に単純な何かが欠けているように思われますが、それが何であるかわかりません。

7
Mr. Minty Fresh

flockは、協調ロックスキームであるアドバイザリロックを行います。これは、協力しなければ、ロックを上書きできることを意味します。操作を実行する前にロックを要求し、完了後にロックを解放することで協力します。ロックによって保護されているのは操作であり、ロックファイル自体ではありません。

私のシステムでは the flock(2) manual から:

アドバイザリロックを使用すると、協調プロセスがファイルに対して一貫した操作を実行できますが、一貫性は保証されません(つまり、プロセスは、アドバイザリロックを使用せずにファイルにアクセスでき、不一致が発生する可能性があります)。

次のスクリプトについて考えてみます。

_#!/bin/sh

( flock -x 9 || exit 1
  echo '1: Locking for 5 secs'; sleep 5; echo '1: Done' ) 9>/tmp/lock &

sleep 1
echo '2: Will now attempt to get lock'

( flock -x 9 || exit 1
  echo '2: Got lock' ) 9>/tmp/lock

# Since the second flock call only performs one operation, the whole last 
# subshell may be replaced by just
#    flock -x /tmp/lock -c echo '2: Got lock'
#
#  (-x and -c are not needed, a lock is exclusive ("write lock")
#   unless -s is used to create a shared lock ("read lock"),
#   and the -c is optional)
_

出力:

_1: Locking for 5 secs
2: Will now attempt to get lock
1: Done
2: Got lock
_

ロックがバックグラウンドプロセスによって取得され、他のflock呼び出しがロックが解放されるのを待ってからロックできることがわかります。

また、ロックファイルはここで保護されるものではないことに注意してください。排他的であることが保証されているのは、サブシェルのecho操作です。特に、ロックファイルは、書き込みまたは読み取りを行う非協調的なプロセスから保護されていません。

つまり、この例では_/tmp/lock_をロックすることにより、各flockサブシェルは、(ファイルまたはその他の共有データリソースに対する)操作がanyからの競合する操作と混ざらないことが保証されます。 _ロックファイルとして_/tmp/lock_を指定したflockを使用する他のプログラム。

上記の最後の段落を説明するために、上のスクリプトを2つの異なる端末(スリープ時間が少し長くなる可能性があります)で可能な限り同時に実行し、2つの競合するスクリプトが適切にロックを取得していることを確認します(相互に待機しています)。 1つのロックがバックグラウンドプロセスで要求されているため、スクリプトの2つのインスタンスを同時に実行すると、ロックかもしれないがスクリプトで指定された順序とは異なる順序で取得されます。

あなたの例では、インタラクティブシェルはロックメカニズムと連携していないです。これが、バックグラウンドのサブシェルによってロックが保持されている場合でも、ファイルの読み取りと書き込みができる理由です。

また、すべてのファイルシステムがflock(またはそれに相当するCライブラリのflock())によるファイルロックをサポートしているとは限らないことにも注意してください。たとえば、ネットワークファイルシステムのAFSとNFSは、この点で問題があるかもしれません。 https://en.wikipedia.org/wiki/File_locking#Problems を参照してください

8
Kusalananda