Unixシステムでは、なぜそれらを `read()`または `write()`できるように `open()`と `close()`ファイルを明示的にしなければならないのですか?


50

なぜopen()およびclose()Unixファイルシステムの設計に存在しますか?

OSが初めて検出したのread()か、write()呼び出されてopen()通常の動作を実行できなかったのでしょうか?


22
このモデルはファイルシステムの一部ではなく、Unix APIの一部であることに注意してください。ファイルシステムは、ディスク上のバイトの行き先やファイル名の配置場所などに関係しているだけです。UFSやext4などのUnixファイルシステムの上に記述した代替モデルを持つことは完全に可能です。これらの呼び出しをファイルシステムの適切な更新に変換するカーネル(現在と同じ)。
-marcelm

18
言いましたように、これはなぜopen()存在するかについての詳細だと思います。 「OSは、read()またはwrite()を初めて検出し、通常open()が行うことは何でもできませんか?」 いつ閉鎖が起こるかについての対応する提案はありますか?
ジョシュアテイラー

7
どのように言うだろうread()か、write()どのファイルにアクセスしますか?おそらくパスを渡すことによって。アクセス中にファイルのパスが変更された場合(2つread()以上のwrite()呼び出しの間)はどうなりますか?
user253751

2
また、通常、アクセス制御はread()およびwrite()では実行しませんopen()
パベルシメルダ16

6
@ジョニー:当時のハードウェアがどれだけ限られていたかを忘れているのかもしれません。Unixが最初に実装されたPDP-7は、(Googleごとに)最大64KのRAMと0.333 MHzのクロックを備えていました-最近では単純なマイクロコントローラーよりも少ないです。このようなガベージコレクションを行うか、システムコードを使用してファイルアクセスを監視すると、システムがひどくなります。
jamesqf

回答:


60

デニス・リッチーはに言及«Unixのタイムシェアリングシステムの進化»ということopenclose一緒にreadwritecreat最初からシステムの右側に存在しました。

私はせずにシステムを推測openし、close私はそれは設計が複雑になると考えているが、想像ではないでしょう。一般に、1つだけでなく複数の読み取り呼び出しと書き込み呼び出しを行いたいと思います。これは、UNIXの起源であるRAMが非常に限られている古いコンピューターで特に当てはまります。現在のファイルの位置を維持するハンドルがあると、これが簡単になります。もしreadまたはwriteハンドルを返すことでした、彼らはペアを返す必要があります-ハンドルと自分の戻りステータス。ペアのハンドル部分は、他のすべての呼び出しには役に立たないため、その配置が厄介になります。カーソルの状態をカーネルに残すことで、バッファリングだけでなく効率を向上させることができます。また、パスルックアップに関連するコストもあります。ハンドルがあると、1回だけ支払うことができます。さらに、UNIXワールドビューの一部のファイルには、ファイルシステムパスさえありません(またはありませんでした-今ではのようになります/proc/self/fd)。


7
パスの検索や許可のチェックなどのコストは非常に重要です。open/を使用せずにシステムを作成しcloseたい場合は、必ず/dev/stdoutパイピングを許可するようなものを実装してください。
ピーターコーデス

5
これの別の側面は、ファイルを開いたままにして複数の読み取りを使用するときに、同じファイルへのハンドルを保持できることです。そうしないと、別のプロセスがリンクを解除し、同じ名前のファイルを再作成し、チャンクでファイルを読み取ることが完全に一貫性のない状態になる可能性があります。(この一部はファイルシステムにも依存します。)
ブルーノ

2
close()なしで設計しました。iノード番号とオフセットをread()とwrite()に渡します。名前解決がそこにあるので、open()なしでは簡単にできません。
ジョシュア

3
@Joshua:UNIXファイル記述子はファイル(inode)を参照せず、特定のファイル(inode)に多数存在する可能性があるファイル記述開くため、このようなシステムは根本的に異なるセマンティクスを持ちます。
R ..

@Joshua、システムの名前open()を変更してget_inode()システム全体の剛性を高めました(複数の位置で同じファイルを同時に読み書きすることはできません)。
フォンブランド

53

その後、すべてのreadおよびwrite呼び出しは、各操作でこの情報を渡す必要があります。

  • ファイルの名前
  • ファイルの許可
  • 呼び出し元が追加または作成中かどうか
  • 呼び出し元がファイルの操作を完了したかどうか(未使用の読み取りバッファーを破棄し、書き込みバッファーが実際に書き込みを終了したことを確認するため)

あなたは独立した考えるかどうかのコールを openreadwriteおよびclose単一目的のI / Oのよりも簡単であることをメッセージがあなたの設計哲学に基づいています。Unix開発者は、すべてを行う単一の操作(またはプログラム)ではなく、さまざまな方法で組み合わせることができる単純な操作とプログラムを使用することを選択しました。


また、ほとんどの場合、呼び出し元はファイル内で目的のオフセットを指定する必要があります。サーバーが状態を維持する必要がなくなるため、各リクエストがファイルとオフセットを個別に識別することが役立つ場合がありますが、いくつかの状況(データへのアクセスを許可するUDPプロトコルなど)がありますが、一般的にはファイルの位置を追跡します。さらに、他の場所で述べたように、ファイルを作成するコードはしばしば事前にそれらをロックし、後でロックする必要があります。これらの操作を開閉と組み合わせることは非常に便利です。
スーパーキャット

5
「ファイル」には、そもそも名前や許可がない場合があります。readまたwrite、ファイルシステムに存在するファイルに限定されるものではありません。これは、pjc50で説明されているように、Unixの基本的な設計上の決定です。
reinierpost

1
また、どこ読み込むファイルに/それを書く-最初、最後、または(通常は最後の読み取り/書き込みが終了した直後であるために)任意の位置-カーネルモードにして(あなたのためにこれを追跡しますすべての書き込みをファイルの最後に向けます。そうでない場合、ファイルは先頭の位置で開かれ、各読み取り/書き込みで進められ、で移動できますlseek
-Random832

51

ファイルハンドルの概念は、ファイルシステムの一部ではないものも含めて、「すべてがファイルである」というUNIXの設計上の選択のために重要です。テープドライブ、キーボードとスクリーン(またはテレタイプ!)、パンチカード/テープリーダー、シリアル接続、ネットワーク接続、および(主要なUNIXの発明)「パイプ」と呼ばれる他のプログラムへの直接接続など。

grep特に元のバージョンのような多くの単純な標準UNIXユーティリティを見るopen()close()readand への呼び出しが含まれておらず、and だけが含まれていることがわかりwriteます。ファイルハンドルは、シェルによってプログラムの外部に設定さ、起動時に渡されます。そのため、プログラムは、ファイルに書き込むのか別のプログラムに書き込むのかを気にする必要はありません。

同様にopen、ファイル記述子を取得する他の方法があるsocketlistenpipedup、、パイプ経由でファイルディスクリプタを送信するために非常にヒース・ロビンソンのメカニズム:https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux -ソケット

編集:インダイレクションの層と、これによりO_APPENDが適切に機能する方法を説明する講義ノート。iノードデータをメモリに保持することで、システムが次の書き込み操作のためにそれらを再度フェッチする必要がなくなることに注意してください。


1
またcreat、およびlistenはfdを作成しませんが、リスニング中にリクエストが着信した場合(およびその場合)accept、新しい(接続された)ソケットのfdを作成して返します。
-dave_thompson_085

18
これが正解です。ファイル記述子に対する有名な(小さな)操作のセットは、データを生成または消費するあらゆる種類のリソースを統合するAPIです。このコンセプトは非常に成功しています。文字列に、リソースタイプと実際の場所(URLは誰か?)を定義する構文考えられますが、使用可能なRAMの数パーセントを占める文字列をコピーするには(PDP 7では何ですか?16 kB?) 。
ピーター-モニカの復活

おそらく、低レベルの呼び出しとシェルが同時に開発された場合、そうなるでしょう。しかしpipe、Unixでの開発が開始されてから数年後に導入されました。
トーマスディッキー

1
@Thomas Dickey:パイプの単純な拡張を許可したため、元の設計がどれだけ優れていたかを示しています&c :
jamesqf

しかし、その議論の行に続いて、この答えは何も新しいものを提供しません。
トーマスディッキー

10

open()とclose()はそれぞれハンドルを作成および破棄するため、答えはノーです。特定のアクセスレベルを持つ唯一の呼び出し元であることを保証したい場合があります(たとえば、実際には常に)、予期せずに解析しているファイルに別の呼び出し元が書き込みを行う可能性があるためです未知の状態にあるアプリケーション、または、食事の哲学者の補題などのライブロックまたはデッドロックにつながるアプリケーション。

その考慮がなくても、考慮すべきパフォーマンスへの影響があります。close()を使用すると、ファイルシステムは(適切な場合または呼び出した場合)占有していたバッファをフラッシュすることができます。これは高価な操作です。インメモリストリームに対する連続した複数の編集は、データセンターに相当する高遅延バルクストレージに散在する世界の半分に存在するファイルシステムに対する、本質的に無関係な複数の読み取り/書き込み/変更サイクルよりもはるかに効率的です。ローカルストレージでも、メモリは通常、バルクストレージよりも桁違いに高速です。


7

Open()は、使用中のファイルをロックする方法を提供します。ファイルがOSによって自動的に開かれ、読み取り/書き込みが行われ、再び閉じられた場合、他のアプリケーションがそれらのファイルを操作間で変更するのを止めることはできません。

これは管理が容易ですが(多くのシステムは非排他的なファイルアクセスをサポートしています)、ほとんどのアプリケーションは、開いているファイルが変更されないと想定しています。


5

ファイルのパスは同じままであると仮定しているときに移動する可能性があるためです。


4

ファイルシステムへの読み取りと書き込みが可能スキームをバッファリングし、OSのハウスキーピング、低レベルのディスク管理、およびその他の潜在的な行動のホストの多種多様を伴います。行動はそうopen()close()、セットアップフード活動の下で、これらのタイプのためとしての役割を果たす。ファイルシステムのさまざまな実装は、必要に応じて高度にカスタマイズでき、呼び出し元のプログラムに対して透過性を維持できます。

OSにオープン/クローズがない場合、with readまたはを使用するとwrite、それらのファイルアクションは、初期化、バッファーのフラッシュ/管理などを毎回実行する必要があります。これは、読み取りと書き込みの繰り返しに課せられる多くのオーバーヘッドです。


open()とclose()がファイル内の位置も保持することを忘れないでください(次の読み取りまたは次の書き込みのため)。そのため、最後にread()およびwrite()にすべてのパラメーターを処理するための構造体が必要になるか、各パラメーターの引数が必要になります。構造の作成は、オープンと同等です(プログラマーサイト)。したがって、OSがオープンについても知っている場合、より多くの利点しかありません。
ジャコモカテナッツィ16

1

Unixのマントラは「物事を行う1つの方法を提供する」、つまり、自由に結合される(再利用可能な)断片に「ファクタリング」することを意味します。つまり、この場合、ファイルハンドルの作成と破棄をその使用から分離します。パイプとネットワーク接続により、後で重要な利点が得られました(これらはファイルハンドルでも操作されますが、他の方法で作成されます)。ファイルハンドルを配布できる(たとえば、それらを子プロセスに "open files"として渡しexec(2)、パイプを介して無関係なプロセスに渡すことも)この方法でのみ可能です。特に、保護されたファイルへの制御されたアクセスを提供する場合。だから、例えば開くことができます/etc/passwd 書き込みのために、それを書き込みのためにそのファイルを開くことを許可されていない子プロセスに渡します(はい、これはばかげた例です。もっとリアルなもので自由に編集してください)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.