この記事では、Linux におけるいくつかの主要なゼロコピー テクノロジと、ゼロコピー テクノロジを適用できるシナリオについて説明します。ゼロコピーの概念をすぐに理解するために、一般的なシナリオを紹介しましょう。 引用 サーバープログラム(Webサーバーやファイルサーバー)を作成する場合、ファイルのダウンロードは基本的な機能です。このとき、サーバーのタスクは、接続されたソケットからサーバー ホスト ディスク上のファイルを変更せずに送信することです。通常、これを完了するには次のコードを使用します。 while((n = read(diskfd, buf, BUF_SIZE)) > 0) 書き込み(sockfd, buf, n); 基本的な操作は、ディスクからファイルの内容をバッファに周期的に読み取り、バッファの内容をソケットに送信することです。ただし、Linux の I/O 操作はデフォルトでバッファリングされた I/O です。ここで主に使用される 2 つのシステム コールは read と write ですが、オペレーティング システムがその中で何を行うかはわかりません。実際、上記の I/O 操作では複数のデータ コピーが発生します。 アプリケーションがデータにアクセスすると、オペレーティング システムはまず、そのファイルが最近アクセスされたかどうか、またそのファイルの内容がカーネル バッファにキャッシュされているかどうかを確認します。キャッシュされている場合、オペレーティング システムは、read システム コールによって提供された buf アドレスに基づいて、カーネル バッファの内容を buf で指定されたユーザー空間バッファに直接コピーします。そうでない場合、オペレーティング システムはまずディスク上のデータをカーネル バッファーにコピーします。カーネル バッファーは現在、主に DMA を使用して転送を行っています。次に、カーネル バッファーの内容をユーザー バッファーにコピーします。 次に、書き込みシステム コールは、ユーザー バッファーの内容をネットワーク スタックに関連付けられたカーネル バッファーにコピーし、最後にソケットはカーネル バッファーの内容をネットワーク カードに送信します。 ここまで述べてきましたが、写真をよく見た方が良いでしょう。 データコピー 上図からわかるように、合計 4 つのデータコピーが生成されます。ハードウェアとの通信に DMA を使用した場合でも、CPU は 2 つのデータコピーを処理する必要があります。同時に、ユーザー モードとカーネル モードの間で複数のコンテキスト スイッチが発生するため、CPU の負荷が確実に増加します。 ゼロコピーテクノロジーとは何ですか? ## ゼロ コピーの主なタスクは、CPU が 1 つのストレージ ユニットから別のストレージ ユニットにデータをコピーするのを防ぐことです。主にさまざまなゼロ コピー テクノロジを使用して、CPU が大量のデータ コピー タスクを実行するのを回避し、不要なコピーを減らしたり、他のコンポーネントにこの種の単純なデータ転送タスクを実行させたりすることで、CPU が他のタスクに集中できるようにします。これにより、システム リソースをより効率的に使用できるようになります。 前回の記事の例に戻りましょう。データのコピー回数を減らすにはどうすればよいでしょうか?明らかな焦点は、カーネル空間とユーザー空間の間でのデータのやり取りを減らすことであり、これにより、ゼロコピーのタイプも導入されます。 ユーザー空間を通過せずにデータ転送を許可する mmap の使用##### コピー数を減らす 1 つの方法は、読み取りではなく mmap() を呼び出すことです。 buf = mmap(diskfd, len); 書き込み(sockfd、buf、len); アプリケーションが mmap() を呼び出すと、ディスク上のデータが DMA 経由でカーネル バッファにコピーされます。その後、オペレーティング システムはこのカーネル バッファをアプリケーションと共有するため、カーネル バッファの内容をユーザー スペースにコピーする必要はありません。アプリケーションは再度 write() を呼び出し、オペレーティング システムはカーネル バッファーの内容をソケット バッファーに直接コピーします。この処理はすべてカーネル状態で行われます。最後に、ソケット バッファーはデータをネットワーク カードに送信します。 mmap read の代わりに mmap を使用すると、明らかに 1 回のコピーが削減され、コピーされるデータの量が多い場合の効率が確実に向上します。しかし、mmap を使用するにはコストがかかります。 mmap を使用すると、隠れた落とし穴に遭遇する可能性があります。たとえば、プログラムがファイルをマップしたが、そのファイルが別のプロセスによって切り捨てられた場合、不正なアドレスにアクセスしたため、書き込みシステム コールは SIGBUS シグナルによって終了します。デフォルトでは、SIGBUS シグナルはプロセスを強制終了し、コアダンプを生成します。サーバーがこのように終了すると、損失が発生します。 この問題を回避するために、通常は次の解決策を使用します。 SIGBUS シグナル用のシグナル ハンドラーを作成する SIGBUS シグナルが発生すると、シグナル ハンドラーは単に戻り、write システム コールは中断される前に書き込まれたバイト数を返し、errno は success に設定されますが、これは問題の核心を解決していないため、対処方法としては適切ではありません。 ファイル リース ロックの使用 通常、この方法はファイル記述子のリース ロックを使用するために使用します。カーネルからファイルのリース ロックを申請します。他のプロセスがファイルを切り捨てる場合、カーネルはリアルタイムの RT_SIGNAL_LEASE シグナルを送信し、カーネルがファイルに対する読み取り/書き込みロックを破棄していることを通知します。この方法では、プログラムが不正なメモリにアクセスして SIGBUS によって強制終了される前に、書き込みシステム コールが中断されます。 write は書き込まれたバイト数を返し、errno を success に設定します。 if(fcntl(diskfd, F_SETSIG, RT_SIGNAL_LEASE) == -1) { perror("カーネルリースセットシグナル"); -1 を返します。 } /* l_type は F_RDLCK F_WRLCK ロックにすることができます*/ /* l_type は F_UNLCK ロック解除にすることができます*/ if(fcntl(diskfd, F_SETLEASE, l_type)){ perror("カーネルリースセットタイプ"); -1 を返します。 } sendfile の使用##### カーネル バージョン 2.1 以降、Linux では操作を簡素化するために sendfile が導入されました。 #include <sys/sendfile.h> ssize_t sendfile(int out_fd, int in_fd, off_t *オフセット, size_t カウント); sendfile() システムコールは、入力ファイル記述子 in_fd と出力ファイル記述子 out_fd の間でファイルの内容 (バイト) を転送します。記述子 out_fd はソケットを参照する必要があり、in_fd が指すファイルは mmap 可能である必要があります。これらの制限により、sendfile の使用は、ファイルからソケットへのデータ転送のみ可能となり、その逆は不可能となります。 sendfile システムコールプロセス sendfile を呼び出したときに別のプロセスがファイルを切り捨てるとどうなりますか?シグナル ハンドラを設定しないと仮定すると、sendfile 呼び出しは単に中断される前に転送されたバイト数を返すだけであり、errno は成功に設定されます。 sendfile を呼び出す前にファイルをロックすると、sendfile は以前と同じように動作し、RT_SIGNAL_LEASE シグナルを受信します。 これまで、データのコピー数は削減されましたが、ページ キャッシュからソケット キャッシュへのコピーがまだ 1 つ残っています。では、このコピーも省略できますか? ハードウェアの助けがあれば、それが実現できます。これまでは、ページ キャッシュ内のデータをソケット キャッシュにコピーしていました。実際には、バッファー記述子をソケット バッファーに渡し、データ長を渡すだけで済みます。このようにして、DMA コントローラーはページ キャッシュ内のデータを直接パッケージ化し、ネットワークに送信できます。 要約すると、sendfile システム コールは DMA エンジンを使用してファイルの内容をカーネル バッファーにコピーし、ファイルの場所と長さの情報を含むバッファー記述子をソケット バッファーに追加します。この手順では、カーネル内のデータはソケット バッファーにコピーされません。DMA エンジンは、カーネル バッファー内のデータをプロトコル エンジンにコピーし、最後のコピーを回避します。 DMA によるファイル送信 ただし、この収集およびコピー機能には、ハードウェアとドライバーのサポートが必要です。 スプライスの使用##### sendfile はファイルからソケットへのデータのコピーにのみ適用可能であり、使用範囲が制限されます。 Linux では、バージョン 2.6.17 で 2 つのファイル記述子間でデータを移動するための splice システム コールが導入されました。 #define _GNU_SOURCE /* feature_test_macros(7) を参照 */ #include <fcntl.h> ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags); スプライス呼び出しは、カーネル空間とユーザー空間間でデータをコピーせずに、2 つのファイル記述子間でデータを移動します。これは、len の長さのデータを fd_in から fd_out にコピーしますが、2 つのうちの 1 つはパイプ デバイスである必要があり、これは現時点での splice の制限の一部でもあります。 flags パラメータには次の値があります。
スプライス呼び出しは Linux によって提案されたパイプ バッファ メカニズムを利用するため、少なくとも 1 つの記述子はパイプである必要があります。 上記のゼロコピー技術はすべて、ユーザー空間とカーネル空間間のデータのコピーを減らすことによって実装されています。ただし、ユーザー空間とカーネル空間間でデータをコピーしなければならない場合もあります。現時点では、ユーザー空間とカーネル空間の間でデータをコピーするタイミングにのみ焦点を当てることができます。 Linux は通常、システムのオーバーヘッドを削減するためにコピーオンライトを使用しており、このテクノロジは COW と呼ばれることがよくあります。 スペースの制約により、この記事ではコピーオンライトについて詳しく説明しません。大まかに言うと、複数のプログラムが同時に同じデータにアクセスする場合、各プログラムにはこのデータへのポインタがあります。各プログラムの観点から見ると、このデータは独立して所有されています。プログラムがデータの内容を変更する必要がある場合にのみ、データの内容がプログラム独自のアプリケーション空間にコピーされます。この時点で、データはプログラムのプライベート データになります。プログラムがデータを変更する必要がない場合は、データを独自のアプリケーション スペースにコピーする必要はありません。これにより、データのコピーが削減されます。コピーオンライトの内容については、別の記事で説明できるほどです。 。 。 さらに、ゼロコピー技術もいくつかあります。たとえば、従来の Linux I/O に O_DIRECT マークを追加すると、直接 I/O を実行し、自動キャッシュを回避できます。また、未熟な fbufs 技術もあります。この記事では、すべてのゼロコピー技術を網羅しているわけではなく、一般的な技術をいくつか紹介するだけです。興味があれば、自分で勉強してください。一般的に、成熟したサーバー プロジェクトでは、データ転送速度を向上させるために、カーネルの I/O 関連部分も独自に変更します。 Linux でのゼロコピー技術の使用に関するこの記事はこれで終わりです。Linux のゼロコピーに関する関連コンテンツをさらにご覧になりたい場合は、123WORDPRESS.COM で過去の記事を検索するか、以下の関連記事を引き続きご覧ください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
>>: MySQL 8.0.21 のインストールと設定方法のグラフィックチュートリアル
目次502 不正なゲートウェイ エラーの発生1. 502 不正なゲートウェイ エラーとは何ですか? ...
目次MySQLの大文字と小文字の区別はパラメータによって制御されますMySQLの大文字と小文字の区別...
目次1. setState() の説明1.1 データの更新1.2 推奨構文1.3 2番目のパラメータ...
目次ユーティリティ: vue での使用:説明する:画像安定化:スロットル:ユーティリティ: // 手...
img 画像タグに alt 属性を追加しますか?画像 img タグの alt 属性を見落とすことはよ...
これが何を意味するのかを理解するには、まずサブディレクトリとは何かを知る必要があります。では、サブデ...
最近、ブルートフォース攻撃によるサーバのクラッキングが頻発しています。侵入行為を大まかに分析し、よく...
目次1. 子プロセス2. nodejsでのコマンド実行2.1 16進数エンコード2.2 ユニコードエ...
目次この期間の目標1. 関数の実装1.1 構造層1.2 スタイルレイヤー1.3 行動層1.3.1 フ...
PHP7が出たので、最新バージョンのファンとしては、早速アップグレードして体験してみました。しかし...
1. スクロールビュー垂直スクロールを使用する場合は、固定の高さを指定して CSS で高さを設定する...
この記事では、3階層ナビゲーションの表示と非表示を実現するためのVueの具体的なコードを例として紹介...
目次1. React.Children.map 2. React.Children.forEach ...
序文: Docker はオープンソースのアプリケーション コンテナ エンジンであり、開発者はこれを使...
目次1. JavaScript オブジェクト1).配列オブジェクト2).ブールオブジェクト3).日付...