open()
とclose()
がUnixファイルシステム設計に存在するのはなぜですか?
OSは、read()
またはwrite()
が初めて呼び出されたときにそれを検出し、open()
が通常行うことをすべて実行できませんでしたか?
Dennis Ritchieが "UNIXタイムシェアリングシステムの進化" に言及しているopen
およびclose
とread
、write
とcreat
は最初からシステムに存在していました。
open
とclose
がないシステムは考えられませんが、設計が複雑になると思います。通常、1回だけでなく複数回の読み取りと書き込みの呼び出しを行いたいと思いますが、これはおそらく、UNIXの元となったRAMが非常に限られている古いコンピューターでは特に当てはまります。現在のファイル位置を維持するハンドルがあると、これが簡単になります。 read
またはwrite
がハンドルを返す場合、ペア(ハンドルと独自の戻りステータス)を返す必要があります。ペアのハンドル部分は、他のすべての呼び出しには役に立たないため、その配置が厄介になります。カーソルの状態をカーネルに任せると、バッファリングだけでなく効率も向上します。また、パスルックアップに関連するコストもあります。ハンドルがあると、一度だけ支払うことができます。さらに、UNIXワールドビューの一部のファイルには、ファイルシステムパスさえありません(またはありません-/proc/self/fd
のようなものを使用しています)。
次に、すべてのread
およびwrite
呼び出しは、各操作でこの情報を渡す必要があります。
独立したcallsopen
、read
、write
およびclose
を単一目的のI /よりも単純であると考えるかどうかO messageは、設計哲学に基づいています。 Unix開発者は、すべてを実行する単一の操作(またはプログラム)ではなく、さまざまな方法で組み合わせることができる単純な操作とプログラムを使用することを選択しました。
UNIXでは、ファイルシステムの一部ではないものも含め、「すべてがファイルである」という設計上の選択があるため、ファイルハンドルの概念は重要です。テープドライブ、キーボードと画面(またはテレタイプ!)、パンチカード/テープリーダー、シリアル接続、ネットワーク接続、および(主要なUNIXの発明)「パイプ」と呼ばれる他のプログラムへの直接接続など。
grep
のような単純な標準UNIXユーティリティの多く、特に元のバージョンを見ると、open()
およびclose()
への呼び出しが含まれていないことに気付くでしょう。ただし、read
とwrite
のみです。ファイルハンドルは、シェルによってプログラムoutsideで設定され、起動時に渡されます。そのため、プログラムはファイルに書き込むのか、別のプログラムに書き込むのかを気にする必要はありません。
open
と同様に、ファイル記述子を取得する他の方法は、socket
、listen
、pipe
、dup
、および非常にヒースです。パイプを介してファイル記述子を送信するためのロビンソンメカニズム: https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux-socket
編集: 一部の講義ノート 間接参照のレイヤーと、これによりO_APPENDが適切に機能する方法を説明します。 inodeデータをメモリに保持することで、システムが次の書き込み操作のためにそれらを再度フェッチする必要がないことが保証されることに注意してください。
Open()とclose()がそれぞれハンドルを作成および破棄するため、答えはノーです。あなたが特定のアクセスレベルを持つ唯一の呼び出し元であることを保証したい場合があります(たとえば、実際には常に)、予期せず解析しているファイルに別の呼び出し元が(たとえば)書き込みを行っていると、不明な状態のアプリケーション、またはライブロックやデッドロックにつながる、たとえばダイニング哲学者の補題。
その考慮事項がなくても、考慮すべきパフォーマンスへの影響があります。 close()を使用すると、ファイルシステムは(適切な場合、または呼び出した場合)、占有していたバッファをフラッシュすることができます。インメモリストリームへのいくつかの連続した編集は、ご存じのように、データセンター全体に分散して存在する高レイテンシのバルクストレージの半分ほど離れた場所に存在する、ファイルシステムに対する本質的に無関係な複数の読み取り/書き込み/変更サイクルよりもはるかに効率的です。ローカルストレージを使用しても、メモリは通常、バルクストレージよりも桁違いに高速です。
Open()は、使用中のファイルをロックする方法を提供します。ファイルが自動的に開かれ、読み取り/書き込みが行われ、OSによって再度閉じられた場合、他のアプリケーションが操作間でそれらのファイルを変更するのを止めることはできません。
これは管理が容易ですが(多くのシステムは非排他的なファイルアクセスをサポートしています)、簡単にするために、ほとんどのアプリケーションは、開いているファイルは変更されないと想定しています。
ファイルのパスは同じままであると想定している間に移動する可能性があるためです。
ファイルシステムの読み書きmayには、さまざまなバッファリングスキーム、OSハウスキーピング、低レベルのディスク管理、およびその他の潜在的なアクションのホストが含まれます。したがって、open()
およびclose()
のアクションは、これらのタイプのボンネットアクティビティのセットアップとして機能します。ファイルシステムのさまざまな実装は、必要に応じて高度にカスタマイズでき、呼び出し元のプログラムに対して透過性を維持できます。
OSに開閉機能がなかった場合、read
またはwrite
を使用すると、これらのファイルアクションで初期化、バッファーのフラッシュ/管理などを毎回実行する必要があります。これは、繰り返しの読み取りと書き込みに課すにはかなりのオーバーヘッドです。
Unixのマントラは「物事を行うための1つの方法を提供する」ことです。つまり、自由に組み合わせる(再利用可能な)部分に「分解」することを意味します。つまり、この場合、ファイルハンドルの作成と破棄を使用とは切り離します。パイプとネットワーク接続により、後で重要なメリットがもたらされました(これらはファイルハンドルを介して操作されますが、他の方法で作成されます)。ファイルハンドルを配布できる(たとえば、それらを「オープンファイル」として子プロセスに渡し、exec(2)
を通過しても、パイプを介して無関係なプロセスにさえ渡す)ことができるのは、この方法のみです。特に、保護されたファイルへの制御されたアクセスを提供したい場合。したがって、たとえば、開いた /etc/passwd
書き込み用に、そのファイルを書き込み用に開くことが許可されていない子プロセスに渡します(はい、これはばかげた例であることを知っています。もっと現実的なもので自由に編集してください)。