ギガバイトのデータがパイプに書き込まれるとどうなりますか?

ギガバイトのデータがパイプに書き込まれるとどうなりますか?

というレシピを含むmakefileがあり、名前が示すhour_long_recipeように、実行に1時間かかるとしましょう。レシピ全体にわたってランダムにはい/いいえ質問をします。合計10個の質問をすると仮定しましょう。

これを実行する1つの可能な(そしてしばしば推奨される)方法は次のとおりです。

yes | make hour_long_recipe

それはすべての質問に答えますy。しかし、私の理解によると、yes出力は常に標準出力に出力されます。1秒あたり最大10.2GiBmake標準入力のデータが実際に使用されているかどうかにかかわらず。

10MiB / s(yesRedditスレッドの実装よりはるかに遅い)に過ぎませんが、1時間以内に合計35GiBを超え、そのうち20バイトのみが読み取られます。データはどこに行きますか?ディスクに保存することは可能ですが、これは無駄であり、ディスクがいっぱいになるとmake障害を引き起こす可能性があります。

おそらくオペレーティングシステムがこれを防ぐようですが、どうすればいいですか?制限は何ですか?その制限に達した後はどうなりますか?

ベストアンサー1

tl;dr: ある時点でyes相手がデータを読み取らない場合、書き込みはブロックされます。データを読み取るか信号を受信するまで実行を続行できないため、通常はギガバイトのyesデータ書き込みについて心配する必要はありません。


覚えておくべき重要な点は管路純粋なストリームではなくFIFOデータ構造で、受信機からすぐに読み取られない場合はデータを削除します。つまり、ほとんどの場合、書き込みアプリケーションから読み取りアプリケーションへのシームレスなデータフローと見なすことができますが、これには中間ストアが必要であり、中間ストアのサイズは制限されています。 *

私たちが見るとPipe(7) マニュアルページ、この内部バッファのサイズに関する次の情報を読み取ることができます(強調追加)。

2.6.11より前のLinuxバージョンでは、パイプ容量はシステムページサイズ(たとえばi386の4096バイト)と同じでした。 Linux 2.6.11以降、パイプ容量は16ページです。(つまり、ページサイズが4096バイトのシステムでは65,536バイトです。)Linux 2.6.35以降、デフォルトのパイプ容量は16ページですが、fcntl(2)F_GETPIPE_SZおよびF_SETPIPE_SZ操作を使用して容量を照会および設定できます。

標準のx86_64システムを使用していると仮定すると、4KiBページを使用する可能性が最も高いため、パイプのいずれかがある時点で使用されていない限り、パイプ容量の上限は2^16でおそらく正確ですfcntl(F_SETPIPE_SZ)。どちらの原則も維持されます。パイプの両側の中間記憶領域は制限され、メモリに記憶される。

抽象パイプラインでは、このリポジトリはa | b一部のデータの書き込みと実際のデータの読み込みの間に使用されます。まあ、あなたの呼び出し(そして継承を介してこのパイプに接続されているすべての子)が実際に標準入力を読み取ろうとしないか、ほとんどそうしないと仮定すると、バッファスペースが枯渇した場合、システムコールは最終的に目覚めません。眠りから。その後、バッファスペースが再利用可能になるか、信号が受信されたときに目が覚めるのを待ちます。 ** これらはすべてカーネルのプロセススケジューラによって処理されます。あなたはできますabmakewriteyesyesyespipe_write()、これはwrite()パイプのハンドラです。

static ssize_t
pipe_write(struct kiocb *iocb, struct iov_iter *from)
{
    /* ... */

    if (pipe_full(pipe->head, pipe->tail, pipe->max_usage))
        wake_next_writer = false;

    if (wake_next_writer)
        wake_up_interruptible_sync_poll(&pipe->wr_wait, EPOLLOUT | EPOLLWRNORM);

    /* ... */
}

そのmake端が最終的に終了すると、パイプに書き込まれた結果として送信され、yesもう一方の端には何も残りません。SIGPIPEその後、yes実装に応じて独自のシグナルハンドラまたはデフォルトのカーネルシグナルハンドラを呼び出して終了します。 ***


*受信者がデータを書き込むのとほぼ同じ速度でデータを処理する簡単な場合、この転送は仮想メモリマップを使用して物理ページを提供することによって中間バッファなしでゼロコピーになることがあります。受信者。ただし、説明する状況では、最終的に未読データを格納するためにパイプバッファを使用する必要があります。

O_NONBLOCK** 書き込みは、非ブロックモードをイネーブルにするファイル記述子に設定されたフラグによっても実行できます。この場合、不完全な書き込みが発生し、書き込みが返され、EAGAINアプリケーションはそれ自体を処理する必要があります。パイプがいっぱいであることを処理するために選択した他のコードを一時停止または実行することでこれを行うことができます。しかし、私が見つけることができるすべての最新バージョンと他のほとんどのアプリではyesO_NONBLOCK

***アプリケーションは、アプリケーションを受け取った後に何でもできますSIGPIPE。理論的には終了しないことを決定することもできます。ただし、すべての一般的なハンドラは、ユーザースペースコマンドを実行せずに単に終了するデフォルトのyesハンドラを使用します。SIGPIPE

おすすめ記事