皆さんこんにちは。私は Linux ディスク上にあるデータです。ここで、ディスクからネットワーク カードに送信するには、次の手順を実行する必要があります:
上に示すように、オペレーティング システムのメモリはカーネル空間とユーザー空間に分割されます。まず、ユーザー空間のアプリケーションがデータ読み取り操作を開始します (JVM が read()
システム コールを開始するなど)。このとき、オペレーティング システムは コンテキスト スイッチ を実行します。ユーザー空間からカーネル空間に切り替えます。
その後、カーネル空間がディスクに通知し、カーネルはディスクからカーネルバッファに私をコピーします。この処理は「DMA(ダイレクト メモリ アクセス)」と呼ばれるハードウェアによって行われるため、CPU の関与は必要ありません。
次に、カーネルは私をカーネル バッファからアプリケーション バッファにコピーします。これには CPU の参加が必要です。
最後にコンテキスト切り替えを実行し、ユーザー空間コンテキストに戻ります。
読み取り操作プロセス全体には、2 つのコンテキスト スイッチと 2 つのコピーが必要です。
関連学習の推奨事項: Java ビデオ チュートリアル
仮想メモリを使用しています。簡単に言うと、 仮想アドレスは物理アドレス を置き換えるために使用されます。これにより、複数の仮想メモリが同じ物理アドレスのみを必要とすることが可能になり、仮想メモリ空間は物理メモリ空間よりもはるかに大きくなる可能性があります。
オペレーティング システムがユーザー空間のアプリケーション バッファーとカーネル空間のカーネル バッファーを同じ物理アドレスにマップできれば、多くのコピー プロセスが不要になるのではないでしょうか?以下に示すように:システム コールは、まず DMA コピーを使用してディスクからカーネル バッファに読み取り、次にメモリ マッピングを使用して ユーザー バッファとカーネル読み取りバッファのメモリ アドレスを同じメモリ アドレスにします。つまり、CPU がカーネル読み取りバッファからユーザー バッファにコピーする必要はありません。
システム コールを使用する場合、CPU は、送信する必要があるカーネル バッファ (ユーザー バッファに相当) からカーネル バッファに直接書き込みます。たとえば、ネットワーク送信バッファ (ソケット バッファ)
を作成し、それを DMA 経由でネットワーク カード ドライバ (またはディスク) に渡し、送信の準備をします。
#mmap 書き込みメソッドでは、データの読み取りと書き込みに、合計 2 つのシステム コール、4 つのコンテキスト スイッチ、2 つの DMA コピー、および 1 つの CPU コピーが必要です。
Linux2.4 カーネルは、sendfile を最適化し、収集操作を提供します。この操作により、上の図の最後の CPU コピーを削除できます。原則として、データをコピーするのではありません。代わりに、 、前のカーネル バッファ (図の場合は読み取りバッファなど) 内のデータのメモリ アドレスとオフセット レコードがターゲットのカーネル バッファ (図の場合はソケット バッファなど) に送信されるため、コピー段階では、このポインタを使用してデータを直接コピーできます。
Linux のゼロ コピーは、実際にオペレーティング システム リソースの一部を節約できます。したがって、Java の NIO は、ゼロ コピーをサポートするためにいくつかのクラスを提供します。
前の「Java NIO - バッファ」では、次のようになります。この記事では DirectByteBuffer について簡単に紹介しています。 ByteBuffer には主に 2 つの実装があり、1 つは DirectByteBuffer で、もう 1 つは HeapByteBuffer です。
このうち、DirectByteBuffer はヒープ外に直接メモリを割り当て、最下位層が JNI を介してオペレーティング システムの NIO システム コールを直接呼び出すため、比較的パフォーマンスが高くなります。 HeapByteBuffer はヒープ内メモリであり、データをもう一度コピーする必要があるため、パフォーマンスは比較的低くなります。
FileChannel
は Java NIO が提供するファイルコピー用のクラスで、ファイルをディスクやネットワークなどにコピーできます。
map
このメソッドは、実際にはオペレーティング システムのメモリ マッピング メソッドを使用して、カーネル バッファのメモリとユーザー バッファのメモリをアドレスにマップします。
transferTo
メソッドは、現在のチャネル コンテンツを別のチャネルに直接転送します。つまり、このメソッドには、カーネル バッファからユーザー バッファへの読み取りおよび書き込みの問題がありません。最下層はsendfileシステムコールです。 transferFrom
方法は同じです。
サンプル コード:
File file = new File("test.txt");RandomAccessFile raf = new RandomAccessFile(file, "rw");FileChannel fileChannel = raf.getChannel();SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("", 8080));// 直接使用了transferTo()进行通道间的数据传输fileChannel.transferTo(0, fileChannel.size(), socketChannel);
著者: パブリック アカウント _xy の技術サークル
リンク: www.imooc.com/article/289550
出典: MOOC。 com
上記のコンテンツは MOOC.com からのものです
ゼロ コピーは、MOOC.com からのものです。運用 システムの観点から。カーネル バッファ間でデータが重複しないためです (カーネル バッファのみにデータのコピーが 1 つあります)。
ゼロ コピーは、データ コピーの量を減らすだけでなく、コンテキストの切り替えの減少、CPU キャッシュの擬似共有の減少、CPU チェックサムの計算の不要など、他のパフォーマンス上の利点ももたらします。
mmap は少量のデータの読み取りと書き込みに適しており、sendFile は大きなファイルの転送に適しています。
mmap には 4 つのコンテキスト スイッチと 3 つのデータ コピーが必要です。sendFile には 3 つのコンテキスト スイッチと少なくとも 2 つのデータ コピーが必要です。
sendFile は DMA を使用して CPU のコピーを減らすことができますが、mmap はできません (カーネルからソケット バッファにコピーする必要があります)。
以上がLinux と Java のゼロコピーについて学ぶの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。