2 ポイント 投稿者 GN⁺ 2024-11-30 | 1件のコメント | WhatsAppで共有

パイプが「止まる」理由: バッファリング

  • 問題の説明: ログファイルから特定の出力を見つけるために tail -f /some/log/file | grep thing1 | grep thing2 コマンドを実行すると、ログ行がゆっくり追加される場合に出力が表示されないことがある。これはパイプが止まっているように見えるが、実際にはプログラムがデータをパイプに書き込んでいないため。

バッファリングの原因

  • バッファリングする理由: プログラムがデータをパイプやファイルに書き込む前にバッファリングするのは一般的。これは性能向上のため、すべての出力を即座に書き込む代わりに、一定量のデータをまとめて一度に書き込むため。
  • : grep thing1 は 8KB のデータがたまるまでマッチしたデータを保持し、その結果、出力が現れないことがある。

ターミナルに書き込むときはバッファリングしない

  • ターミナルとパイプの違い: grep は出力先がターミナルのときは行バッファリングを使うが、パイプのときはブロックバッファリングを使う。これは isatty 関数によって判定される。

バッファリングするコマンドとしないコマンド

  • バッファリングしないコマンド: tail, cat, tee などはバッファリングしない。
  • バッファリングするコマンド: grep, sed, awk, tcpdump, jq, tr, cut などはバッファリングし、一部は特定のフラグでバッファリングを無効化できる。

プログラミング言語の標準出力バッファリング

  • バッファリングする言語: C, Python, Ruby, Perl などは標準で出力をバッファリングし、特定の方法で無効化できる。

Ctrl-C を押したときのバッファ内容の損失

  • 問題の説明: Ctrl-C を押すとバッファ内の内容が失われる。これは SIGINT シグナルが先に送られるため。
  • 解決策: tcpdump の PID を見つけて kill -TERM $PID を実行すれば、バッファをフラッシュできる。

ファイルへリダイレクトするときもバッファリングされる

  • ファイルへのリダイレクト: ファイルへリダイレクトするときもバッファリングは発生するが、Ctrl-C によるバッファ損失の問題は起きない。

バッファリングを避けるさまざまな方法

  • 解決策 1: すぐ終了するプログラムを実行する。
  • 解決策 2: grep--line-buffered フラグを使う。
  • 解決策 3: awk を使う。
  • 解決策 4: stdbuf を使う。
  • 解決策 5: unbuffer を使う。

バッファリングを無効化する環境変数

  • アイデア: PYTHON_UNBUFFERED のような標準的な環境変数があるとよい、という意見。NO_BUFFER のような変数が提案されている。

省略された内容

  • 省略されたトピック: 行バッファリングと完全な非バッファリングの違い、stderr と stdout のバッファリングの違い、OS の TTY ドライバによるバッファリングなど。

1件のコメント

 
GN⁺ 2024-11-30
Hacker Newsの意見
  • バッファリングされたアクセスは、一定のバイト数に達したり一定時間が経過したらフラッシュすべき。これはハードウェアインターフェースで似た問題を解決する一般的な方法

    • ユーザー空間でバッファリングするライブラリは、データを最初にバッファした時点で適切なタイマーを設定する必要がある
    • タイムアウトのパラメータは引数として渡すか、人間の時間感覚より少し短めにするか、帯域幅/しきい値に比例させるか、フラッシュのオーバーヘッドに比例させるのがよい
    • 書き込みと読み取りの両方に当てはまり、データチャネルごとに異なる場合がある
  • システム全体のCPUがアイドル状態になったら、すべてのバッファをフラッシュしたい

    • バッファリングは一般にCPUを節約するための手法
    • CPUがアイドル状態になったら、すべてのプロセスに「バッファをフラッシュせよ」という信号を送るべき
  • NIXシステムを20年以上扱ってきたが、バッファリングの問題はいつも忘れてしまう

  • Unixを35年以上使ってきたが、バッファリングの動作を完全には理解していなかった。この説明は有益だった

  • 「非バッファリング」と「行バッファリング」を混同している

    • 非バッファリングは性能を低下させる可能性があり、複数のソースが同じパイプに書き込む場合は誤った出力を生成することがある
    • 行バッファリングはターミナルのデフォルトであり、パイプにも適している
  • バッファが存在するのは、画面に出力を表示するよりもバッファへ書き込むほうが相対的に非常に遅いから

    • UART作業時によくある問題で、さまざまな解決策がある
    • 特殊文字の使用、長さベースのアプローチ、時間ベースのアプローチなど、さまざまな方法がある
  • Ctrl-Cを押すと、バッファの内容が失われる可能性がある

    • ほとんどのプログラムは、SIGINTでバッファをフラッシュすると思っていた
  • Unixでバッファリングの問題に遭遇したことがあり、すべてのawk実装が同じように動作するわけではない

  • 凍りついたパイプのジョークを見逃した気分だ