2

以下は、大学のタスクに対する小さなプログラムです。

#include <unistd.h>

#ifndef BUFFERSIZE
#define BUFFERSIZE 1
#endif

main()
{
    char buffer[BUFFERSIZE];
    int i;
    int j = BUFFERSIZE;

    i = read(0, buffer, BUFFERSIZE);

    while (i>0)
    {
        write(1, buffer, i);
        i = read(0, buffer, BUFFERSIZE);
    }

    return 0;
}

代わりに stdio.h fread および fwrite 関数を使用する代替手段があります。

良い。この両方のバージョンのプログラムを 25 の異なる値のバッファー サイズ: 1、2、4、...、2^i with i=0..30 でコンパイルしました。

これは私がそれをコンパイルする方法の例です: gcc -DBUFFERSIZE=8388608 prog_sys.c -o bin/psys.8M

質問: 私のマシン (Ubuntu Precise 64、詳細は最後に) では、プログラムのすべてのバージョンが正常に動作します: ./psys.1M < data

(データは 3 行の ASCII テキストを含む小さなファイルです。)

問題は、バッファ サイズが 8MB 以上の場合です。両方のバージョン (システム コールまたは clib 関数を使用) は、これらのバッファー サイズでクラッシュします (セグメンテーション フォールト)。

私は多くのことをテストしました。コードの最初のバージョンは次のようなものでした: (...) main() { char buffer[BUFFERSIZE]; int i;

    i = read(0, buffer, BUFFERSIZE);
(...)

読み取り関数を呼び出すと、これがクラッシュします。しかし、これらのバージョンでは:

main()
{
    char buffer[BUFFERSIZE]; // SEGMENTATION FAULT HERE
    int i;
    int j = BUFFERSIZE;

    i = read(0, buffer, BUFFERSIZE);


main()
{
    int j = BUFFERSIZE; // SEGMENTATION FAULT HERE
    char buffer[BUFFERSIZE];
    int i;

    i = read(0, buffer, BUFFERSIZE);

どちらもメインの最初の行でクラッシュします (SEGFAULT)。ただし、バッファーをメインからグローバル スコープに移動すると (したがって、スタックではなくヒープに割り当てられます)、これは正常に機能します。

char buffer[BUFFERSIZE]; //NOW GLOBAL AND WORKING FINE
main()
{
    int j = BUFFERSIZE;
    int i;

    i = read(0, buffer, BUFFERSIZE);

私は Ubuntu Precise 12.04 64 ビットと Intel i5 M 480 第 1 世代を使用しています。

#uname -a
Linux hostname 3.2.0-34-generic #53-Ubuntu SMP Thu Nov 15 10:48:16 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

スタックに関する OS の制限がわかりません。これは良い方法ではありませんが、大きなデータをスタックに割り当てる方法はありますか?

4

2 に答える 2

3

非常に単純だと思います。バッファを大きくしすぎると、スタックがオーバーフローします。

malloc()バッファの割り当てに使用する場合は、問題はないと思います。

alloca()スタックストレージを明示的に割り当てると呼ばれる関数があることに注意してください。使用alloca()は、プログラムの実行中に発生することを除いて、スタック変数の宣言とほとんど同じです。マニュアルページを読んalloca()だり、それについて議論したりすると、プログラムの何が問題になっているのかを理解するのに役立つ場合があります。ここに良い議論があります:

alloca()の使用が良い習慣と見なされないのはなぜですか?

ulimit編集:コメントの中で、@ jim mcnamaraは、ユーザー制限をチェックできるコマンドラインツールについて教えてくれました。このコンピューター(Linux Mint 14を実行しているため、Ubuntu 12.10と同じ制限である必要があります)では、ulimit -sコマンドはスタックサイズの制限を表示します:8192 Kバイト。これは、説明したとおりに問題をうまく追跡します。

編集:完全に明確でない場合は、を呼び出して問題を解決することをお勧めしますmalloc()。もう1つの受け入れ可能な解決策は、メモリを静的に割り当てることです。これは、コードがシングルスレッドである限り正常に機能します。

スタックを爆破したくないという理由だけで、小さなバッファにのみスタック割り当てを使用する必要があります。バッファが大きい場合、またはコードが関数を何度も再帰的に呼び出して、小さなバッファが何度も割り当てられる場合は、スタックが破壊され、コードがクラッシュします。最悪の部分は、警告が表示されないことです。問題がないか、プログラムがすでにクラッシュしています。

の唯一の欠点は、比較的遅いため、タイムクリティカルなコード内malloc()で呼び出したくないことです。malloc()ただし、初期設定には問題ありません。mallocが完了すると、割り当てられたバッファのアドレスは、プログラムのアドレス空間内の他のメモリアドレスと同様にメモリ内のアドレスになります。

システムのデフォルトを編集してスタックサイズを大きくすることは特にお勧めしません。これにより、プログラムの移植性が大幅に低下します。malloc()コードをWindows、Mac、Androidなどにほとんど問題なく移植できるような標準Cライブラリ関数を呼び出す場合。システム関数を呼び出してデフォルトのスタックサイズを変更し始めると、移植でさらに多くの問題が発生します。(そして、これを今すぐ移植する計画はないかもしれませんが、計画は変わります!)

于 2012-12-17T03:56:20.460 に答える
1

Linux では、多くの場合、スタック サイズが制限されています。このコマンドulimit -sは、現在の値をキロバイトで表示します。デフォルトは (通常) ファイルで変更できます/etc/security/limits.conf。権限に応じて、コードを使用してプロセスごとに変更することもできます。

#include <sys/resource.h>
// ...
struct rlimit x;
if (getrlimit(RLIMIT_STACK, &x) < 0)
    perror("getrlimit");
x.rlim_cur = RLIM_INFINITY;
if (setrlimit(RLIMIT_STACK, &x) < 0)
    perror("setrlimit");
于 2012-12-17T04:30:44.753 に答える