
2026/01/09 0:36
**「ファイルロックについて、あなたが決して知りたかった全て(2010)**」
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
(以下に日本語訳を示します)
Summary
この記事では、Unix のファイルロック API ―
flock()、fcntl()、および lockf() ― をポータビリティ、意味論、および信頼性の観点から比較しています。
は使用が簡単ですが POSIX 標準ではありません。そのため、一部のシステムでサポートされていない場合があり、NFS 上での動作も必ずしも安定していません。また、flock()
の際にロックは継承されます(ただし、プラットフォームによってはfork()
を介した実装になることがあります)。fcntl()
は POSIX に準拠する選択肢です。バイト範囲ロックをサポートし、fcntl(F_SETLK)
でロック所有者を報告でき、ある inode のいずれかのディスクリプタが閉じられると、その inode に対するすべてのロックが解放されます。ただし、ロックはアドバイザリー(他プロセスをブロックしない)であり、フォーク時に継承されません。子プロセスは親のロックを一切持ちません。NFSv3+ や SMB 上の Mac OS X ではリモートロッキングが不安定になることがあります。F_GETLK
はlockf()
の薄い POSIX ラッパーですが、ポータビリティは限定的で(いくつかの BSD 系で欠如)、クエリ機能(fcntl()
)を持ちません。F_GETLK- 同一ファイルに対してロックタイプ(
、flock()
、またはfcntl()
)を混在させると未定義動作が発生しますので、避けてください。強制ロックも存在しますが Linux では信頼性が低く、一般的には推奨されません ― アドバイザリロックの使用が勧められます。lockf() - Python では
モジュールがすべての API を公開していますが、fcntl
を直接使用する場合は構造体パッキングを手動で行う必要があり(特に 64 ビットオフセットの場合)、ポータビリティが低くなります。Python でファイルロックを行う慣用的な方法はfcntl.fcntl()
です。これは内部で packedfcntl.lockf()
を使用しています。struct flock - Mac OS 10.6(バグ #8760769)には、バイト範囲
ロック使用時に静かにデータ破損を引き起こす既知のバグがありました。最近の OS 更新でこの挙動は修正されています。fcntl()
主なポイント:プラットフォームに適した API を選択してください ― サポートされているシステムでは簡潔さを求めるなら
flock()、POSIX 準拠が必要ならアドバイザリ fcntl() または Python の lockf()。ロックタイプの混在はデータレースや破損を招くため避けてください。特にネットワークファイルシステム上では注意が必要です。本文
ファイルロックについて、知りたかった全てのこと
先読み: MacOS X 10.6 の
fcntl(F_SETLK) に SQLite データベースを破損させるバグがあることを発見しました。locky.c をコンパイルして実行し、システムをテストしてください。
私は明示的なファイルロックに依存したプログラムを書いたことはなく、gdbm のようなデータベースは API 背後で隠していますが、Unix 系のロッキング API は極めて難解でシステム間(同一 OS 内でも)不整合が多く、信頼性に欠けることがよくあります。
以下では Unix スタイルの 3 種類のロックについて簡潔にまとめます。
1. flock()
flock()- シンプル – ファイル全体をロックします。
- ロック種別 – 共有 (
) と排他 (LOCK_SH
)。LOCK_EX - 動作 – 「リーダー/ライター」スタイル:複数の読者、1 つの書き手。
- 制限点
- POSIX 標準ではなく、一部 SysV ライクなシステムにはありません。
- NFS 上で機能しません。
- 共有から排他への切替は競合が起こりやすい(ロックを解放して再取得)。
- 古い Linux バージョンでは
を使ってfcntl()
をエミュレートしており、奇妙な挙動が継承されます。flock()
2. fcntl()
fcntl()-
標準化 – POSIX がほぼ全 Unix で利用可能を保証しています。
-
バイト範囲ロック – 各ロックは開始位置と長さを指定でき、範囲が独立します。
-
アドビザリ – カーネルは警告のみ;プログラム側が協調する必要があります。
-
NFS サポート – 信頼性が低く、カーネルごとに挙動が異なります。
-
SMB の制限 – MacOS X (10.6) では SMB マウント上で
ロックが失敗します。fcntl() -
主なクイックポイント
- ロックは
ペアに紐づく;その inode を参照する任意のディスクリプタを閉じると、プロセス全体のロックが解放されます。(pid, inode) - 子プロセスへはロックが継承されません。
はstruct flock
とは無関係です。flock()
- ロックは
-
実践的なヒント
- 共有ロックを排他ロックに原子操作でアップグレードできます。
を使えば所有者 PID を取得でき、デバッグに便利です。F_GETLK
3. lockf()
lockf()- POSIX 定義 ですが、BSD 系のバリエーションでは必ずしも実装されていません。
- 通常は
の薄いラッパーで、ロック所有者を問い合わせる手段がありません。fcntl() - 推奨 – ターゲットシステムで確実にサポートされていることが分かっていない限り使用しないでください。
ロック種別間の相互作用
同一ファイルに対して複数のロック種別(
flock()、fcntl()、lockf())を併用すると 未定義動作 になります。 macOS では統合実装が存在するため無害に見えることもありますが、移植性は損なわれます。フォーマットが必要とするロック機構を明示的に文書化し、それに従うようにしてください。
強制ロック vs. アドビザリロック
- 強制ロック はカーネルレベルでアクセス制御を課します。 Linux では脆弱で信頼性が低く、微妙な競合状態を引き起こす可能性があります。
- アドビザリロック(全ての API のデフォルト)は合理的な選択です。
- 協調プログラムに依存します。
- 強制ロックは必要不可欠でない限り避けるべきです。
Python におけるファイルロック
Python の
fcntl モジュールは全ての Unix ロック種別を公開していますが、注意点があります。
| C API | Python 等価 |
|---|---|
| (内部で を使用する場合もあります) |
| – の pack/unpack が必要;非移植的。 |
| – 実際には をラップしています。 |
推奨
- C では
を使用し、fcntl()
は避ける。lockf() - Python では
を使う(内部でfcntl.lockf()
が呼ばれます)。fcntl()- 注意:Python からロック所有者を問い合わせる直接的な方法はありません。
要約
| ロック種別 | 対応環境 | 回避すべき理由 |
|---|---|---|
| POSIX 準拠システム、NFS 以外 | 標準化されておらず、NFS や一部 SMB マウントで失敗。 |
| 一部 Unix(主に BSD) | 普遍的ではなく、 の薄いラッパー。 |
| ほぼ全 Unix/Linux/macOS | 古い macOS では SMB 上で不安定;inode‑PID セマンティクスが奇妙。 |
複数プラットフォームで確実にロックを行いたい場合は、ロックファイル を利用するか、これらの詳細を抽象化した高レベルライブラリを検討してください。
*このガイドは 2026 年初頭時点のファイルロック状況を反映しています。更新(例:新しい
F_OFD_SETLK サポート)については、最新 OS ドキュメントをご参照ください。