1. ホーム
  2. c

[解決済み] C言語でバッファリングを理解する

2022-03-02 23:37:16

質問

特にC言語プログラミングにおけるバッファリングの奥深さを理解するのに苦労しており、このトピックについて本当に長い間検索しましたが、今まで満足できるものが見つかりませんでした。

もう少し具体的に説明します。 バッファリングの背後にあるコンセプト(異なるハードウェアデバイスによる操作の調整や、これらのデバイスの速度差の最小化)は理解していますが、これらやその他のバッファリングの潜在的な理由について、より完全な説明をお願いします(完全というのは、より長く、より深いということです)。

他の質問としては、バッファフラッシュにおけるいくつかのルールが私のプログラムによって守られていないことに気づいたことです。

#include <stdio.h>

int main(void)
{
    FILE * fp = fopen("hallo.txt", "w");

    fputc('A', fp);
    getchar();
    fputc('A', fp);
    getchar();

    return 0;
}

このプログラムは、差し迫った入力が最初の getchar() が呼ばれたときに直ちに任意のストリームをフラッシュすることを示すためのものですが、何度やっても、何度修正しても、これは単に stdout (この場合 printf() このため、私はこのルールを間違って理解しているのか、それとも他に考慮すべきことがあるのでしょうか。

Windows 8.1 で Gnu GCC を使用しています。

更新しました。

そういえば、あるサイトで、文字列リテラルをバッファと呼んだり、配列をバッファと呼んだりしているのを読んだのですが、これは正しいのでしょうか、それとも私が何か見逃しているのでしょうか? この点についても解説をお願いします。

どのように解決するのですか?

単語 バッファ は、コンピュータサイエンスにおいて様々な意味で使われています。より一般的な意味では、最終目的地(または他のバッファ)に処理またはコピーされるまでの間、データを一時的に保存するメモリの部分を指します。

質問にあったように、バッファには多くの種類がありますが、大まかなグループ分けとして

  1. ハードウェア・バッファ。ハードウェアデバイスに移動する前にデータが格納されるバッファです。または、HWデバイスから受信したデータをアプリケーションで処理するまで保存しておくバッファです。これは、I/O操作には通常メモリとタイミングの要件があり、バッファがそれを満たすために必要です。メモリに直接読み書きするDMAデバイスを考えてみてください。メモリが適切にセットアップされていないと、システムがクラッシュする可能性があります。また、マイクロ秒以下の精度が必要なサウンドデバイスでは、動作が悪くなります。

  2. キャッシュバッファー。ファイルやデバイスに書き込んだり、デバイスから読み込んだりする前にデータをグループ化するバッファで、一般的にパフォーマンスが改善されます。

  3. ヘルパーバッファ。このようなバッファにデータを出し入れするのは、アルゴリズムにとって簡単だからです。

ケース 2 は、あなたの FILE* の例です。を呼び出したとします。 書く システムコール( WriteFile() Win32の場合、呼び出しだけで1ms、1バイトごとに1usかかります(現実にはもっと複雑です)。では、そうすると。

FILE *f = fopen("file.txt", "w");
for (int i=0; i < 1000000; ++i)
    fputc('x', f);
fclose(f);

バッファリングがない場合、このコードでは 1000000 * (1ms + 1us) , 約1000秒になります。しかし、10000バイトのバッファがあれば、10000バイトずつのシステムコールが100回だけ発生することになります。そうすると 100 * (1ms + 10000us) . わずか0.1秒です!

また、OSが独自のバッファリングを行い、最も効率的なサイズを使って実際のデバイスにデータを書き込むことに注意してください。これは、HWとキャッシュバッファを同時に行うことになりますね。

フラッシュに関する問題ですが、ファイルは通常、閉じたときか手動でフラッシュされるだけです。いくつかのファイル、例えば stdout はラインフラッシュされます。 '\n' が書き込まれます。また stdin/stdout は特別です。 stdin では stdout がフラッシュされます。他のファイルはそのままで stdout . これは、インタラクティブなプログラムを書いている場合に便利です。

私のケースその3は、例えばこんな時です。

FILE *f = open("x.txt", "r");
char buffer[1000];
fgets(buffer, sizeof(buffer), f);
int n;
sscanf(buffer, "%d", &n);

バッファを使ってファイルから1行を保持し、その行からデータをパースするのです。そうです。 fscanf() 行の種類を分析したり、コメントをスキップしたり、行数を数えたり...。

また、例えばキーボードから一度に1バイトずつ受け取る場合を想像してみてください。バッファに文字を蓄積しておき、Enterキーが押されたときにその行を解析するだけです。ほとんどの対話型コンソールプログラムがそうなっています。