ファイルロックとは、コンピュータファイル、またはファイル内の特定の領域へのアクセスを制限する仕組みであり、特定の時間に1つのユーザーまたはプロセスのみがファイルを変更または削除できるようにし、変更または削除中はファイルの読み取りを防止します。
システムは、競合状態の一例である、更新処理の割り込みを防ぐためにロック機構を実装し、特定のファイルへの更新処理の直列化を強制します。次の例は、割り込み更新の問題を示しています。
ほとんどのオペレーティングシステムはレコードロックの概念をサポートしており、これは特定のファイル内の個々のレコードをロックできるため、同時更新プロセス数を増やすことができることを意味します。データベースのメンテナンスではファイルロックが使用され、データベースの基盤となる物理ファイル全体へのアクセスをシリアル化できます。これにより、他のプロセスがファイルにアクセスすることはできなくなりますが、ファイル内の多くの領域を個別にロックするよりも、各ロックの取得と解放のオーバーヘッドがなくなるため、効率的になります。
ファイルロックは、他のコンピュータロックと同様に、不適切な使用によってパフォーマンスの低下やデッドロックを引き起こす可能性があります。ファイルロックとは、Windowsセキュリティ、NTFSアクセス許可、またはサードパーティ製のファイルロックソフトウェアのインストールなど、コンピュータユーザーが適用する追加のセキュリティ対策を指す場合もあります。
IBMは1963年にOS/360を使用するメインフレームコンピュータ向けにファイルロックを先駆的に導入し、それを「排他制御」と呼んだ。[ 1 ]
Microsoft Windowsは、共有ファイルへのアクセスを管理するために3つの異なるメカニズムを使用します。
Windowsは、共有アクセス制御のセマンティクスをMS-DOSシステムから継承しています。共有機能はMS-DOS 3.3で導入されました 。そのため、アプリケーションはファイルを開く際に明示的に共有を許可する必要があります。そうしないと、ファイルが閉じられるまで、アプリケーションはファイルへの排他的な読み取り、書き込み、削除アクセス権を持つことになります(ファイルの属性を取得するなどの他の種類のアクセスは許可されます)。
共有アクセスで開かれたファイルの場合、アプリケーションはバイト範囲ロックを使用して、ファイルの特定領域へのアクセスを制御できます。このようなバイト範囲ロックでは、ファイルの領域(オフセットと長さ)とロックの種類(共有または排他)を指定します。ロック対象のファイル領域にはデータが含まれている必要はなく、アプリケーションはこの機能を利用して独自の機能を実装することがあります。
Windows のファイル読み書きAPIを使用するアプリケーションでは、バイト範囲ロック (強制ロックとも呼ばれます) が Windows 内で実行されるファイルシステムによって強制されます。Windows のファイルマッピングAPI を使用するアプリケーションでは、バイト範囲ロックは強制されません (アドバイザリロックとも呼ばれます)。バイト範囲ロックは、Windows システムに他の副作用をもたらす場合もあります。たとえば、Windows のファイル共有メカニズムでは、いずれかのクライアントがバイト範囲ロックを使用すると、通常、すべてのクライアントに対してファイルのクライアント側キャッシュが無効になります。読み取りおよび書き込み操作をファイルが保存されているサーバーに送信する必要があるため、クライアントはアクセス速度の低下を実感することになります。
アプリケーションプログラムのエラー処理が不適切だと、ファイルがロックされ(「共有」アクセスまたはバイト範囲ファイルロックによる)、他のアプリケーションからアクセスできなくなる場合があります。このような場合、ユーザーは問題のあるプログラムを手動で終了させることで、ファイルへのアクセスを回復できる可能性があります。これは通常、タスクマネージャユーティリティを使用して行います。
ファイルを開く際に使用する[ 2 ]関数の共有モード(dwShareMode)パラメータは、ファイルの共有方法を決定します。共有モードを指定することで、読み取り、書き込み、削除、またはこれらの組み合わせによるファイルの共有を許可できます。その後、ファイルを再度開く場合は、以前に許可されたすべての共有アクセスと互換性がある必要があります。ファイルが閉じられると、共有アクセスの制限が調整され、その特定のファイルのオープンによって課せられた制限が解除されます。CreateFile
バイト範囲ロックの種類は、ファイルの領域をロックするために使用される[ 4 ]dwFlags関数のパラメータによって決定されます。Windows API関数[ 5 ]も使用でき、ファイルの領域に対する排他ロックを取得します。LockFileExLockFile
コンピュータ システム上でプログラムとして実行中の実行可能プログラム ファイル (たとえばEXE、 、COM、DLL、CPLまたはその他のバイナリ プログラムファイル形式) を含むファイルは、通常、オペレーティングシステム自体によってロックされ、アプリケーションによる変更や削除が防止されます。プログラム ファイルがどのアプリケーションによっても開かれていない場合でも、変更や削除を試みると共有違反エラーが発生して拒否されます。ただし、一部のアクセスは許可されています。たとえば、実行中のアプリケーション ファイルは、実行中でも名前を変更したり、コピー (読み取り) したりできます。
Windowsでは、アプリケーションはファイルハンドルを使用してファイルにアクセスします。これらのファイルハンドルは、 Process Explorerユーティリティで調べることができます。このユーティリティを使用すると、ハンドルを保持しているアプリケーションを終了させることなく、ハンドルを強制的に閉じることもできます。ただし、強制的に閉じられたハンドルを使用するとプログラムが予期しないエラーを受け取る可能性があり、ハンドル番号が再利用される可能性があるため、予期しないファイルに対して操作を行う場合もあるため、未定義の動作が発生する可能性があります。
Microsoft Windows XPおよびServer 2003エディションでは、 NTFSにボリューム スナップショット( ) 機能が導入され、排他ロックがかかっていてもバックアップ ソフトウェアが開いているファイルにアクセスできるようになりました。ただし、この機能を明示的にサポートするようにソフトウェアが書き換えられない限り、スナップショットはクラッシュ整合性のみとなります。一方、適切にサポートされているアプリケーションは、オペレーティングシステムが「トランザクション整合性」のあるスナップショットを作成するのを支援できます。Windows でロックされたファイルにアクセスするための他の商用ソフトウェアには、File Access ManagerやOpen File Managerなどがあります。これらは、カーネル モードでファイルにアクセスするための独自のドライバをインストールすることで機能します。VSS
Unix ライクなオペレーティングシステム ( Linuxや Apple のmacOSを含む) では、通常、開いているファイルを自動的にロックしません。Unix のさまざまなバージョンには、いくつかの種類のファイル ロック メカニズムが用意されており、多くのオペレーティングシステムは互換性のために複数の種類をサポートしています。最も一般的なメカニズムは です。他の 2 つのメカニズムはと で、それぞれ の上に実装することfcntlも、 とは別に実装することもできますfcntl。一部の種類のロックは強制的に構成できますが、Unix のファイル ロックはデフォルトではアドバイザリです。これは、協力するプロセスはロックを使用してファイルへのアクセスを調整できますが、非協力的なプロセスはロックを無視して任意の方法でファイルにアクセスすることもできます。言い換えれば、ファイル ロックは他のファイル ロッカーのみをロックアウトし、I/O はロックアウトしません。
2 種類のロックが提供されます。共有ロックと排他ロックです。 の場合fcntl、異なる種類のロックをファイルの異なるセクション (バイト範囲) またはファイル全体に適用できます。共有ロックは複数のプロセスが同時に保持できますが、排他ロックは 1 つのプロセスのみが保持でき、共有ロックと共存することはできません。共有ロックを取得するには、プロセスは排他ロックを保持しているプロセスがなくなるまで待機する必要があります。排他ロックを取得するには、プロセスはどちらの種類のロックも保持していないプロセスがなくなるまで待機する必要があります。 によって作成されたロックとは異なりfcntl、 によって作成されたロックはflock間で保持されるforkため、フォーク サーバーで役立ちます。したがって、複数のプロセスが同じファイルに対して排他ロックを保持することが可能です。ただし、これらのプロセスが子関係を共有し、排他ロックが 間で複製される前に単一のプロセスで最初に作成されたことが条件ですfork。
共有ロックは「読み取りロック」、排他ロックは「書き込みロック」と呼ばれることがあります。しかし、Unix のロックは勧告的なものであるため、この区別は強制されません。したがって、データベースには「共有書き込み」と「排他書き込み」という概念が存在する可能性があります。例えば、フィールドのインプレース変更は共有アクセスで許可される一方、ガベージコレクションやデータベースの書き換えには排他アクセスが必要になる場合があります。
ファイルロックはファイル名ではなく、実際のファイルに適用されます。これは、Unixでは同じファイルを複数の名前で参照できるため重要です。非強制ロックと組み合わせることで、複数のプロセスからファイルにアクセスする際の柔軟性が大幅に向上します。一方で、協調ロック方式では、他のプロセスによって設定されたファイルロックに従わずにプロセスがファイルに書き込む場合に問題が発生する可能性があります。
このため、一部の Unix ライクなオペレーティングシステムでは、強制ロックの限定的なサポートも提供されています。[ 6 ]このようなシステムでは、setgidファイルが開かれたときにビットがオンでグループ実行ビットがオフになっているファイルは、基盤となるファイルシステムがサポートしていれば自動的に強制ロックの対象となります。ただし、非ローカル NFS パーティションはこのビットを無視する傾向があります。[ 7 ]ファイルが強制ロックの対象となっている場合、排他ロックでロックされている領域から読み取ろうとしたり、共有ロックまたは排他ロックでロックされている領域に書き込もうとしたりすると、ロックが解除されるまでブロックされます。この戦略は System V で初めて採用され、現在ではSolaris、HP-UX 、Linux オペレーティングシステムで見られます。ただし、POSIX の一部ではなく、FreeBSD、OpenBSD、NetBSD、Apple のmacOSなどの BSD 派生オペレーティングシステムではサポートされていません。[ 8 ] Linux では、ファイルシステムのマウント用の特別なパラメータ( ) を介して強制ロックもサポートされていますが、これはほとんど使用されていません。-o mand
Unix ライクなオペレーティングシステムの中には、実行中のプログラムの実行可能ファイルを書き込み用に開こうとする試みを阻止するものがあります。これは、およびによって提供されるロックとは異なる、3 つ目のロック形式fcntlですflock。
flock排他ロックが後続のプロセス間で複製された場合、複数のプロセスが特定のファイルに対して排他ロックを保持できますfork。これはネットワークサーバーのコーディングを簡素化し、競合状態を防ぐのに役立ちますが、知らない人にとっては混乱を招く可能性があります。
強制ロックは に影響を与えませんunlink。したがって、特定のプログラムは、事実上、強制ロックを回避する可能性があります。Stevens & Rago (2005) は、edエディタが実際にそうしていることを観察しました。[ 9 ]
ネットワークファイルシステム( NFSflockなど)でロックがどのように機能するかは、実装に依存します。BSDシステムでは、NFSマウントされたパーティション上のファイルに対して開かれたファイルディスクリプタへの呼び出しは、成功して何もしません。Linux 2.6.12より前のバージョンでは、NFSファイルへの呼び出しはローカルでのみ機能します。カーネル2.6.12以降では、NFSファイルへの呼び出しはPOSIXバイト範囲ロックを使用して実装されています。これらのロックは、 POSIXスタイルのロックを実装している他のNFSクライアントからは見えますが、そうでないクライアントからは見えません。[ 10 ]flockflock flockfcntl
ロックのアップグレードとダウングレードでは、新しいロックを適用する前に古いロックが解放されます。あるアプリケーションが排他ロックを共有ロックにダウングレードしている間に、別のアプリケーションが排他ロックを待機してブロックされている場合、後者のアプリケーションが排他ロックを取得し、前者のアプリケーションをロックアウトしてしまう可能性があります。つまり、ロックのダウングレードによってブロックが発生する可能性があり、これは直感に反するかもしれません。
fcntl特定のプロセスがファイルに関連付けられたすべてのロックは、そのファイルディスクリプタに対してロックが要求されていなくても、そのプロセスによってそのファイルに関連付けられたファイルディスクリプタが閉じられると削除されます。また、ロックはfcntl子プロセスに継承されません。このfcntlクローズセマンティクスは、ファイルにアクセスする可能性のあるサブルーチンライブラリを呼び出すアプリケーションにとって特に厄介です。これらの「バグ」は、実際のスタイルのロックでは発生しませんflock。
Unixドメインソケットを使用して別のプロセスに渡されるオープンファイルディスクリプタのロック状態の保持は、実装に依存します。
ロック失敗の原因の 1 つは、バッファ付き I/O で、オペレーティングシステムのバッファ プールではなく、ユーザーのローカル ワークスペースにバッファが割り当てられている場合です。freadと はfwriteバッファ付き I/O によく使用され、ファイルの一部が読み取られると、同じ部分を読み取ろうとする別の試みでは、ほとんどの場合、ローカル バッファからデータが取得されます。問題は、同じファイルに接続されている別のユーザーが独自のローカル バッファを持っており、同じことが彼らにも起こっていることです。fwriteによってバッファから取得されたデータのは、ファイル自体からデータを取得するわけではfreadないためflock、他のユーザーがそれを変更している可能性があります。両方とも を使用して排他アクセスを確保し、同時書き込みを防ぐことができますが、読み取りはファイル自体ではなくバッファから読み取られるため、ユーザー #1 によって変更されたデータはユーザー #2 によって失われる (上書きされる) 可能性があります。この問題に対する最善の解決策は、 と でバッファなし I/O (readとwrite)を使用することです。これは、との代わりにflockを使用することも意味します。もちろん、関数のパラメータと返される結果を調整する必要があります。一般的に言って、バッファリングされたI/Oは共有ファイルで使用すると安全ではありません。lseekfseekftell
AmigaOSでは、ファイル (またはディレクトリ) のロックは、Lock関数 ( 内dos.library) を使用して取得できます。ロックは共有 (他のプロセスはファイル/ディレクトリを読み取ることができますが、変更または削除することはできません) または排他的 (ロックを正常に取得したプロセスのみがオブジェクトにアクセスまたは変更できます) にすることができます。ロックはオブジェクト全体にかかり、その一部にかかりません。ロックは関数を使用して解放する必要がありますUnLock。Unix とは異なり、オペレーティングシステムはプロセスの終了時にオブジェクトのロックを暗黙的に解除しません。
シェルスクリプトやその他のプログラムでは、ファイルロックと同様の手法がよく用いられます。それは、ロックファイルの作成です。ロックファイルの内容は重要ではありません(ただし、多くの場合、ロックを保持しているプロセスの識別子が含まれています)。ロックファイルの唯一の目的は、その存在によって何らかのリソースがロックされていることを示すことです。制御対象のリソースが通常のファイルではない場合、つまりファイルロックの手法が適用できない場合は、ロックファイルを使用するのが最善策となることがよくあります。例えば、ロックファイルは、複数の異なるファイル、ディレクトリ、ディスクパーティションのグループ、あるいはサーバーやデータベース接続などの上位レベルのプロトコルへの特定のアクセスなど、関連するリソース群へのアクセスを制御することができます。
ロックファイルを使用する場合は、操作がアトミックであることを確実に保証する必要があります。ロックを取得するには、プロセスはロックファイルが存在しないことを確認してから作成する必要がありますが、その間、他のプロセスがロックファイルを作成しないようにする必要があります。これを行うためのさまざまな方法には、次のものがあります。
lockfileパッケージに同梱されているコマンド(条件付きセマフォファイル作成ツールprocmail)を使用します。mkdir、失敗の終了コードを確認します[ 11 ]ロックファイルは、ロック対象ファイル名の前にチルダ(~ ~)を付けるか、ファイル名の末尾にチルダを付けて命名されることが多い.LCK 。ファイル以外のリソースをロックする場合は、より恣意的な命名が用いられることもある。
アンロッカーは、どのプロセスがファイルをロックしているかを特定するために使用されるユーティリティで、プロセスの一覧と、そのプロセスに対して行う操作(タスクの強制終了、ロック解除など)の選択肢、および削除や名前変更などのファイルオプションの一覧を表示します。その目的は、不適切または古いファイルロックを削除することです。これらのロックは、クラッシュやハングアップなどの異常な状況によって発生することが多く、所有プロセスが既に終了しているにもかかわらず、ファイルロックが残存してしまうことがあります。一部のUnix系システムでは、などのユーティリティを使用してfstat、lockfプロセス別、ファイル名別、またはその両方でファイルロックの状態を調べることができます。
Windowsシステムでは、ファイルがロックされている場合、次回の再起動時にそのファイルの移動または削除を実行するようにスケジュール設定できます。この方法は通常、インストーラーがロックされたシステムファイルを置き換える際に使用されます。
バージョン管理システムでは、ファイルロックは、2人のユーザーが同じファイルバージョンを並行して変更し、保存時に2人目のユーザーが1人目のユーザーの変更を上書きしてしまうことを防ぐために使用されます。これは、ロックされたファイルをファイルシステム上で読み取り専用としてマークすることで実現されます。ファイルを変更したいユーザーは、ロック解除(チェックアウトとも呼ばれます)操作を実行し、チェックイン(保存)操作が完了するか、ロックが解除されるまで、他のユーザーはファイルのロックを解除できません。
Rust は、、、、lockメソッドをtry_lock使用したファイルロックをサポートしています。[ 12 ]lock_sharedtry_lock_sharedunlock
use std :: fs :: File ;fn main () - > std :: io :: Result < () > { let f = File :: create ( "foo.txt" ) ?; f.lock ( ) ?; Ok (( ) ) }.NET は . を使用したファイルロックをサポートしていますFileStream。[ 13 ]
System.IOを使用します。var textLength = 1 ; var byteCount = 1 ;using var stream = new FileStream ( "foo.txt" , FileMode . OpenOrCreate ); stream . Lock ( textLength - 1 , byteCount );PHP は関数を使用してファイルロックをサポートしていますflock。[ 14 ]
$fp = fopen ( "/tmp/lock.txt" , "r+" );if ( flock ( $fp , LOCK_EX )) { // 排他ロックを取得ftruncate ( $fp , 0 ); // ファイルを切り詰めるfwrite ( $fp , "ここに何か書き込んでください\n " ); fflush ( $fp ); // ロックを解放する前に出力をフラッシュするflock ( $fp , LOCK_UN ); // ロックを解放する} else { echo "ロックを取得できませんでした!" ; }fclose ( $fp );CreateFileWLockFileExLockFileExLockFileSetuidSetgidとSolarisはどちらも強制ロックをサポートしていますが、Darwin、FreeBSD、NetBSD、OpenBSDは、LinuxとSolarisが強制ロックをサポートするために使用するインターフェースを公開しているにもかかわらず、サポートしていません。これらのシステムでは、このインターフェースによってアドバイザリロックが作成されます。NFSは強制ロックをサポートしていません。