ホームページ  >  記事  >  運用・保守  >  IOバッファ管理例を詳しく解説

IOバッファ管理例を詳しく解説

PHP中文网
PHP中文网オリジナル
2017-06-20 13:19:502480ブラウズ

Linux システム IO の書き込みプロトタイプは ssize_t write(int filedes, const void * buff, size_t nbytes);

write を呼び出してデータを書き込む場合、write は呼び出し完了後に直接戻りますが、ディスクデバイスが遅い場合、オペレーティング システムはデータをカーネル内のバッファに保存し、そのデータをディスクに非同期で書き込む責任を負います。もちろん、このときにシステムがダウンするとデータは失われます。 Write はシステム コールであり、各呼び出しはカーネルをトラップするため、適切なブロック長の buffsize を選択し、その呼び出しを最小限に抑えることで効率を最適化できます。 ANSI C の標準 IO では、printf/fprintf/fputs などを呼び出すと、標準 IO ライブラリが処理するため、write のように buffsize を選択するのではなく、ストリームに書き込むだけで済みます。バッファ割り当て、最適化された長さでの IO の実行など、多くの詳細を提供します。これにより、書き込み/読み取りシステム コールの数が減り、効率が向上します。しかし同時に、データのコピーという別の問題も発生します。たとえば、関数 fgets と fputs を使用する場合、通常は 2 つのバッファを経由する必要があります。1 つは標準の IO バッファで、もう 1 つはカーネル バッファです。読み取りと書き込みを呼び出します。ただし、一般に、標準 IO を使用すると、システム IO よりもインターフェイスが簡単になり、効率も同等になります。

標準 IO は、フル キャッシュ、行キャッシュ、キャッシュなしの 3 種類のバッファを提供します。フル キャッシュは、バッファがいっぱいになった場合にのみアクティブにフラッシュされ、通常はディスク ファイルの IO に使用されます。ライン キャッシュは、バッファ内で改行文字が検出されたときにフラッシュされます。また、入力データを標準入出力から取得する必要がある場合にも、ライン キャッシュは対話型端末で使用されます。キャッシュを使用しない場合は、システム コール出力を直接書き込むことと同じであり、標準エラー ストリーム stderr は通常キャッシュされないため、エラー メッセージをできるだけ早く表示できます。デフォルトのフラッシュ条件に加えて、fflush 関数が明示的に呼び出され、プログラムが正常に終了した場合にもバッファはフラッシュされます。 setbuf/setvbuf を使用してデフォルトのバッファ長を変更できます。APUE セクション 5.4 を参照してください。

標準IOを使用するプログラムでは、標準出力をファイルにリダイレクトするとラインキャッシュがフルキャッシュとなり、場合によってはprintf("***の呼び出しなど)予期しないエラーが発生する可能性があります。 **n")、プログラムが対話的に実行されると通常の出力が発生します。ただし、標準出力がファイルにリダイレクトされると、バッファ領域が完全にキャッシュされ、printf は正常に出力されず、データ行はバッファ内に残ります。このとき子プロセスをフォークすると、データ空間が子プロセスにコピーされるときに、バッファデータも子プロセスにコピーされます。その後、子プロセスで出力が実行されると、バッファー内の以前の内容が更新され、予期しない出力が発生します。

ネットワーク プログラミングでは、システム IO を直接使用する必要があります。標準 IO では、パフォーマンスを向上させるためにバッファリング メカニズムが導入されており、これによりネットワーク アプリケーションが複雑になります。また、標準のIOストリームはある意味全二重であり、入出力を同時に行うことができますが、ストリームの制限とソケットの制限が矛盾する場合があります。 (CSAPP P611 を参照)

一部の高度なネットワーク ライブラリ (Muduo ライブラリなど) は、システム IO の使用に基づいて独自のバッファを作成し、ユーザーがシステム IO の不都合な点 (書き込みを呼び出して送信するなど) をシールドできるようにします。大量のデータがあるため、送信バッファがいっぱいになるとアプリケーション層は待機する必要があり、read がデータを受信するとパケットがスティッキーになり、データの受信が遅くなります。アプリケーション層バッファが追加されると、ネットワーク ライブラリはこれらの実装の詳細を処理して、ユーザーの操作を簡素化します。

Linux は、メモリのコピーを削減し、効率を向上させるゼロコピー テクノロジも提供しています。読み取り/書き込みを使用してディスクからネットワーク カードにデータを送信すると、次の 4 つのコピー操作が実行されることがわかっています。カーネルは、同じファイルへの以前のアクセスにより、データがオペレーティング システムのカーネル アドレス空間のバッファに格納されているかどうかを最初に確認します。データがカーネル バッファに見つからない場合は、 Linux オペレーティング システム カーネルは、まずこのデータをディスクから読み取り、オペレーティング システム カーネルのバッファに置きます。このデータ読み出し動作が DMA によって完了する場合、CPU は DMA によるデータ読み出しのプロセス中にバッファ管理を実行し、DMA を作成および処理するだけで済み、その他の多くの変更を行う必要はありません。つまり、DMA はデータ読み取り操作を実行した後、さらなる処理を行うようオペレーティング システムに通知します。 Linux オペレーティング システムは、ユーザーがデータに対する操作を完了した後、読み取りシステム コールで指定されたアプリケーション アドレス空間のアドレスに基づいて、このデータを要求したアプリケーションのアドレス空間にこのデータを保存します。オペレーティング システムは、ユーザー アプリケーションのアドレス空間のバッファからネットワーク スタックに関連するカーネル バッファにコピーを作成する必要があります。このプロセスにも CPU 使用率が必要です。データのコピー操作が完了すると、データはパッケージ化されてネットワーク インターフェイス カードに送信されます。上記の説明からわかるように、この従来のデータ転送プロセスでは、DMA を使用してハードウェアと通信する場合でも、データは少なくとも 4 回コピーされます。CPU は依然としてデータに 2 回アクセスする必要があります。

(追記: 以前、printf 出力プロセスが複数のバッファーを通過するというインタビューの質問を読んだ記憶があります。今では誰もがそれを理解しています!)

ゼロコピー テクノロジを使用すると、システム内のデータのバッファリングを回避できますカーネルアドレス空間領域とユーザーアプリケーションアドレス空間バッファーがコピーされます。場合によっては、アプリケーションはデータ送信プロセス中にデータにアクセスする必要がなく、送信されたデータをユーザー アプリケーション領域にコピーする必要がなく、カーネルを通じてネットワーク カードに直接送信できるため、パフォーマンスが向上します。現時点ではゼロコピー技術が必要です。 Linux では、mmap、sendfile、splice を使用してゼロコピーを実現できます。

以上がIOバッファ管理例を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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