
質問
パス名 にディレクトリ以外のもの (ファイル、名前付きパイプ/ソケットなど) があり/tmp/foo
、 パス名 に別のディレクトリ以外のものがあるとします/tmp/bar
。 この場合、2 つ (またはそれ以上) のプロセスが同時に実行を開始します。
プロセス 1 では次のことが行われます。
unlink('/tmp/foo') /* or rename('/tmp/foo', '/tmp/removed') */
unlink('/tmp/bar') /* or rename('/tmp/bar', '/tmp/removed') */
プロセス 2 (など) は次のことを行います。
link('/tmp/foo', '/tmp/bar')
私の理解では、プロセス 2 が成功する可能性はありません ( がまだ存在するlink(2)
間に が試行され/tmp/foo
、その場合は/tmp/bar
も存在するため で失敗するEEXIST
か、/tmp/foo
がなくなったため で が失敗するかのいずれかですENOENT
)。
unlink(2)
しかし、この直感は、 and/orrename(2)
システム コールのリンク解除効果が本質的に連続的であるという仮定に依存しているため、私は自分の理解の検証を求めています。カーネルが 2 つのunlink(2)
and/orrename(2)
呼び出しの成功を許可し、同時に もlink(2)
成功させる ( および のリンク解除の順序を変更し、 を呼び出すプロセスからそれを抽象化/非表示にしないためか/tmp/foo
、/tmp/bar
またはlink(2)
その他の奇妙な競合状態/バグによるか) ような *nix のようなシステムはありますか?
現在の理解
私はLinuxといくつかのBSDの、、のマニュアルページと、これらの関数のPOSIX仕様を読みましたunlink(2)
。しかし、よく考えてみると、これらのマニュアルには、この問題に関して安心できる内容は何も書かれていないと思います。少なくとも、では、rename(2)
link(2)
rename(2)
行き先すでに存在する場合は自動的に置き換えられます(OS自体のバグは別として)、他には何もありません。
私は見た請求を複数同時に実行すると、rename(foo, qux)
アトミックかつ移植性を保ちながら、1 つの名前変更以外はすべて失敗します。これは期待できます。ただし、同じ状況でが失敗するENOENT
ように拡張できるかどうかは不明です。link(foo, bar)
ENOENT
推奨回答
これは「否定を証明できない」状況の 1 つであることは承知しています。せいぜい、プロセス 2 を成功させる *nix のようなシステムが存在するという証拠がないということしか指摘できませんlink(2)
。
私が求めているのは、できるだけ多くの *nix 系システム (少なくとも Linux、OS X、さまざまな BSD ですが、理想的には Solaris 10 のようなまだ一部で使用されているプロプライエタリ システムも) を網羅した回答です。回答者は、これらのシステムと、この狭い範囲の問題 (アトミック/整然としたファイル システム操作) に十分精通しており、(現実的に可能な限り) 前述の Mac OS X の実際にはrename(2)
アトミックではないバグのような問題が、自分が精通しているプラットフォームに存在していたとしても、それを知っているはずだと確信している人です。そうすれば、これが私が考えているように、十分に移植性があり、信頼できる方法で動作するという十分な自信が得られます。
最終ノート
これは「X/Y 問題」の質問ではありません。さまざまなロック/IPC メカニズムや、これらの特定のシステム コールがどのように相互作用するかについての不確実性を回避する他の方法を参照することで回答できる根本的な問題はありません。具体的には、上記のシステム コールが、現在実際に使用されている *nix 系システム間で期待どおりに移植可能に相互作用できるかどうかを知りたいのです。
答え1
次のような基準を見てくださいPOSIX移植性の保証のため。実際には、ほとんどの POSIX 準拠システムは仕様からわずかに逸脱していますが、一般的に言えば、仕様で与えられた保証に頼ることができます。ほとんどの最新の unice は、正式にテストされていなくても仕様に準拠しています。たとえば、bash で設定するか、Solaris でより進んでいることを確認するなど、POSIX モードで実行する必要がある場合がPOSIXLY_CORRECT=1
あります。/usr/xpg4/bin
/bin
/usr/bin
PATH
シングル Unix v2(POSIXの古い拡張)は次のように述べている。link
:
この
link()
関数は、既存のファイルに対して新しいリンクを自動的に作成し、ファイルのリンク数が 1 増加します。
についてrename
:
リンク名が新しい引数が存在する場合は削除され、古いに改名新しいこの場合、newという名前のリンクは、名前変更操作中も他のプロセスから見えるままになり、新しいまたは古い手術が始まる前に。
POSIXは、宛先が存在する場合、その置換はアトミックでなければならないと明示的に規定している。しかし、名前の変更自体がアトミックでなければならない、つまり、両方が同時に実行される時点はない、とは規定していない。古いそして新しい問題のファイルを参照している場合、またはどちらも参照していない場合。実際には、これらのプロパティは、少なくともローカルファイルシステムでは、UNIX システムでは当てはまります。
さらに、演算の順序が保証されます。Cでは、;
は順次実行を保証します。shでは、;
/newlineは(do&&
などと同様に)順次実行を保証します。他のプログラミング言語でも同様の保証があります。
unlink("/tmp/foo");
unlink("/tmp/bar");
/tmp/foo
が存在するが、存在しない時点は存在しないことが保証されています/tmp/bar
(最初は が存在すると仮定/tmp/bar
)。したがって、並行プロセスの実行はlink("/tmp/foo", "/tmp/bar")
成功しません。
原子性は保証するものではないことに注意回復力原子性は、ライブ システムで観察可能な動作に関するものです。ファイルシステムのコンテキストにおける復元力は、システム クラッシュが発生した場合に何が起こるかに関するものです。多くのファイルシステムはパフォーマンスのために復元力を犠牲にしているため、 の実行が中断された場合(現在のディレクトリがディスク ストレージ上にある場合)、 が削除されて残ってしまうunlink("foo"); unlink("bar");
可能性があります。bar
foo
一部のネットワーク ファイルシステムでは、異なるクライアントで操作が行われる場合の保証が少なくなります。NFS の古い実装では、この点で悪名高いものでした。最新の実装の方が優れていると思いますが、最新の NFS の経験はありません。