ホームページ  >  記事  >  Java  >  Java I/O は内部でどのように動作するのでしょうか?

Java I/O は内部でどのように動作するのでしょうか?

伊谢尔伦
伊谢尔伦オリジナル
2016-11-26 09:10:031121ブラウズ

このブログ投稿では主に、I/O が最下位レベルでどのように機能するかについて説明します。この記事は、Java I/O 操作がマシン レベルでどのようにマップされるか、およびアプリケーションの実行時にハードウェアが何を行うかを理解したい読者に役立ちます。 Java I/O API を介したファイルの読み取りおよび書き込みなど、基本的な I/O 操作に精通していることを前提としています。これらの内容はこの記事の範囲外です。

Java I/O は内部でどのように動作するのでしょうか?

キャッシュ処理とカーネルvsユーザー空間

バッファリングとバッファリング処理方法は、すべてのI/O操作の基礎です。 「入力、出力」という用語は、データをキャッシュに出入りする場合にのみ意味を持ちます。このことを常に念頭に置いてください。通常、オペレーティング システムへの I/O 要求を実行するプロセスには、バッファからのデータの排出 (書き込み操作) とバッファへのデータの充填 (読み取り操作) が含まれます。これが I/O の全体的な概念です。オペレーティング システム内でこれらの転送操作を実行するメカニズムは非常に複雑になる場合がありますが、概念的には非常に単純です。それについては、記事の小さな部分で説明します。

Java I/O は内部でどのように動作するのでしょうか?

上の画像は、ブロック データがディスクなどの外部ソースからプロセスの記憶域 (RAM など) にどのように移動されるかを表す単純化された「論理」図を示しています。まず、プロセスは read() システム コールを通じてバッファーを埋める必要があります。このシステム コールにより、カーネルはディスク制御ハードウェアにコマンドを発行して、ディスクからデータを取得します。ディスク コントローラは、メイン CPU の支援を必要とせずに、DMA 経由でカーネルのメモリ バッファにデータを直接書き込みます。 read() 操作が要求されると、ディスク コントローラーがキャッシュへの書き込みを完了すると、カーネルはカーネル空間の一時キャッシュからプロセス固有のキャッシュにデータをコピーします。

注意すべき点の 1 つは、カーネルがデータのキャッシュとプリフェッチを試行するとき、カーネル空間内のプロセスによって要求されたデータはすでに準備ができている可能性があるということです。その場合、プロセスによって要求されたデータがコピーされます。データが利用できない場合、プロセスは一時停止されます。カーネルはデータをメモリに読み取ります。

仮想メモリ

仮想メモリについては何度も聞いたことがあるかもしれません。改めてご紹介させていただきます。

最新のオペレーティングシステムはすべて仮想メモリを使用します。仮想メモリとは、物理 (ハードウェア RAM) メモリ アドレスではなく、人工アドレスまたは仮想アドレスを意味します。仮想アドレスには 2 つの重要な利点があります:

複数の仮想アドレスを同じ物理アドレスにマッピングできます。

仮想アドレス空間は、実際に利用可能なハードウェア メモリよりも大きくなる場合があります。

上記の紹介では、カーネル空間からエンドユーザーキャッシュへのコピーにより、余分な作業が追加されるように見えます。データをユーザー空間のキャッシュに直接送信するようにディスク コントローラーに指示してはどうでしょうか?まあ、これは仮想メモリによって実装されています。上記のメリット1を活用しましょう。

カーネル空間アドレスをユーザー空間仮想アドレスと同じ物理アドレスにマッピングすることにより、DMA ハードウェア (物理メモリ アドレスのみをアドレス指定できる) がキャッシュを埋めることができます。このキャッシュは、カーネル プロセスとユーザー空間プロセスの両方に表示されます。

Java I/O は内部でどのように動作するのでしょうか?

これにより、カーネルとユーザー空間の間のコピーが排除されますが、カーネルとユーザーのバッファーが同じページ配置を使用する必要があります。バッファーは、ディスク コントローラーのブロック サイズ (通常は 512 バイトのディスク セクター) の倍数を使用する必要があります。オペレーティング システムは、メモリ アドレス空間を固定サイズのバイト グループであるページに分割します。これらのメモリ ページは常にディスク ブロック サイズの倍数で、通常は 2 倍 (簡素化されたアドレス指定) です。一般的なメモリ ページ サイズは 1024、2048、および 4096 バイトです。仮想メモリと物理メモリのページ サイズは常に同じです。

メモリ ページング

仮想メモリの 2 番目の利点 (物理メモリよりも大きなアドレス指定可能な空間を持つ) をサポートするには、仮想メモリ ページング (ページ スワッピングと呼ばれることが多い) が必要です。このメカニズムは、仮想メモリ空​​間内のページを外部ディスク ストレージに保存できるという事実に依存しており、それによって他の仮想ページを物理メモリに配置するためのスペースが提供されます。基本的に、物理メモリはページング領域のキャッシュとして機能します。ページング領域は、メモリ ページが物理メモリから強制的にスワップアウトされるときにメモリ ページの内容が保存されるディスク上のスペースです。

メモリ ページ サイズをディスク ブロック サイズの倍数に調整して、カーネルがディスク コントローラ ハードウェアに命令を直接送信して、メモリ ページをディスクに書き込むか、必要に応じて再ロードできるようにします。すべてのディスク I/O 操作がページ レベルで行われることがわかります。これは、最新のページング オペレーティング システムでディスクと物理メモリ間でデータを移動できる唯一の方法です。

最新の CPU には、メモリ管理ユニット (MMU) と呼ばれるサブシステムが含まれています。このデバイスは論理的に CPU と物理メモリの間に配置されます。これには、仮想アドレスから物理メモリ アドレスへのマッピング情報が含まれています。 CPU がメモリの場所を参照すると、MMU はどのページを常駐させる必要があるかを判断し (通常、アドレスの特定のビットをシフトまたはマスクすることによって)、仮想ページ番号を物理ページ番号に変換します (ハードウェアで実装され、非常に高速です)。

ファイル指向、ブロックI/O

ファイルI/Oは常にファイルシステムのコンテキストスイッチで発生します。ファイルシステムとディスクはまったく別のものです。ディスクはデータをセグメントに保存します。各セグメントは 512 バイトです。これはハードウェア デバイスであり、保存されたファイルのセマンティクスについては何も知りません。データを保存できる特定の数のスロットを提供するだけです。この点で、ディスク セグメントはメモリ ページングに似ています。それらはすべて均一なサイズを持ち、1 つの大きなアドレス指定可能な配列です。

一方、ファイル システムはより高いレベルの抽象化です。ファイル システムは、ディスク (またはその他のランダム アクセス、ブロック指向のデバイス) に保存されているデータを配置および変換する特別な方法です。作成したコードは、ほとんどの場合、ディスクと直接やり取りするのではなく、ファイル システムとやり取りします。ファイル システムは、ファイル名、パス、ファイル、ファイル属性などの抽象概念を定義します。

ファイルシステムは、均一なサイズの一連のデータブロックを(ハードディスク上で)編成します。一部のブロックは、空きブロック、ディレクトリ、インデックスなどのマッピングなどのメタ情報を保持します。他のブロックには実際のファイル データが含まれます。個々のファイルのメタ情報は、ファイルのデータがどのブロックに含まれているか、データがどこで終了するか、最後に更新されたのはいつかなどを記述します。ユーザー プロセスがファイル データの読み取り要求を送信すると、ファイル システムはディスク上のデータの場所を正確に特定します。次に、これらのディスク セクタをメモリに配置するアクションが実行されます。

ファイルシステムにはページという概念もあり、そのサイズは基本メモリのページサイズと同じかその倍数になります。一般的なファイル システムのページ サイズの範囲は 2048 ~ 8192 バイトで、常にベース メモリ ページ サイズの倍数になります。

ページ ファイル システムでの I/O の実行は、次の論理的な手順に要約できます:

どのファイル システム ページ (ディスク セグメントのコレクション) にリクエストがまたがっているかを決定します。ディスク上のファイル コンテンツとメタデータは、複数のファイル システム ページに分散されている場合があり、これらのページは連続していない場合があります。

同じファイル システム ページを保持するのに十分なカーネル空間メモリ ページを割り当てます。

これらのメモリ ページとディスク上のファイル システム ページのマッピングを確立します。

すべてのメモリページに対してページフォルトを生成します。

仮想メモリ システムはページング フォールトに陥り、ディスクから内容を読み取ってそれらのページを検証するページジンをスケジュールします。

ページインが完了すると、ファイル システムは生データを分解して、要求されたファイルのコンテンツまたは属性情報を抽出します。

このファイル システム データは他のメモリ ページと同様にキャッシュされることに注意してください。後続の I/O リクエストでは、ファイル データの一部またはすべてが物理メモリに残り、ディスクから再読み取りすることなく直接再利用できます。

ファイルロック

ファイルロックは、プロセスが他のプロセスがファイルにアクセスできないようにしたり、他のプロセスがファイルにアクセスするのを制限したりできるメカニズムです。 「ファイルロック」という名前ですが、これはファイル全体をロックすることを意味します(これはよく行われます)。通常、ロックはより詳細なレベルで実行できます。粒度がバイト レベルに低下すると、ファイルの領域がロックされることがよくあります。ロックは特定のファイルに関連付けられ、ファイル内の指定されたバイト位置から始まり、指定されたバイト範囲まで実行されます。これにより、他のプロセスがファイル内の他の場所で動作するのを妨げることなく、複数のプロセスが協力してファイルの特定の領域にアクセスできるようになるため、これは重要です。

ファイルロックには共有と排他の 2 つの形式があります。複数の共有ロックを同じファイル領域で同時に有効にすることができます。一方、排他的ロックでは、要求された領域に対して他のロックが有効でないことが必要です。

ストリームI/O

すべての I/O がブロック指向であるわけではありません。パイプのプロトタイプであるストリーム I/O もあり、I/O データ ストリームのバイトには順番にアクセスする必要があります。一般的なデータ フローには、TTY (コンソール) デバイス、印刷ポート、ネットワーク接続が含まれます。

データストリームは通常、ブロックデバイスよりも遅く、断続的な入力を提供しますが、必ずしもそうではありません。ほとんどのオペレーティング システムでは、ノンブロッキング モードでの作業が可能です。プロセスが、データ ストリームへの入力が利用可能かどうかを、そうでない場合はブロックせずにチェックできるようにします。この管理により、プロセスは入力を到着時に処理し、入力ストリームがアイドル状態である間に他の機能を実行できるようになります。

ノンブロッキングモードからさらに進んだのが、条件付き選択(準備性選択)です。これはノンブロッキング モードに似ています (多くの場合、ノンブロッキング モードに基づいて構築されます) が、ストリームの準備ができているかどうかを確認するオペレーティング システムの負担から解放されます。オペレーティング システムは、ストリームのコレクションを監視し、どのストリームが準備できているかをプロセスに指示を返すように指示できます。この機能により、プロセスは、オペレーティング システムから返された準備情報を活用することで、共通のコードと単一のスレッドを使用して複数のアクティビティ ストリームを再利用できます。この方法は、多数のネットワーク接続を処理するためにネットワーク サーバーで広く使用されています。大規模な拡張には、選択の準備が重要です。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。