44

GCC 3.4.5 (mingw-special vista r3) で MinGW を使用しています。

私の C アプリケーションは多くのスタックを使用するので、スタックが不足しそうになった場合に状況をきれいに処理できるように、プログラムでスタックがどれだけ残っているかを知る方法はないかと考えていました。

そうでない場合、スタックスペースが不足する可能性があるという問題を回避する方法は他にありますか?

どのサイズのスタックから始めればよいのかわからないので、プログラムでもそれを特定する必要があります。

4

9 に答える 9

30

getrusage 関数は、現在の使用状況を取得します。( を参照man getrusage)。

getrlimitLinux では、パラメーターを使用してスタック サイズを取得するのに役立ちますRLIMIT_STACK

#include <sys/resource.h>
int main (void)
{
  struct rlimit limit;

  getrlimit (RLIMIT_STACK, &limit);
  printf ("\nStack Limit = %ld and %ld max\n", limit.rlim_cur, limit.rlim_max);
}

をご覧くださいman getrlimitulimit -s同じ情報が、またはulimit -aスタック サイズの行によってフェッチされる可能性があります。またsetrlimit、制限を設定できる機能も見てください。ただし、他の回答で述べたように、スタックを調整する必要がある場合は、おそらく設計を再検討する必要があります。大きな配列が必要な場合は、ヒープからメモリを取得してみませんか?

于 2011-05-03T03:37:29.410 に答える
18

スタックからローカル変数のアドレスを取得するとうまくいきます。次に、よりネストされた呼び出しで、別のローカルのアドレスを減算して、それらの違いを見つけることができます

size_t top_of_stack;

void Main()
{
  int x=0;
  top_of_stack = (size_t) &x;

  do_something_very_recursive(....)
}

size_t SizeOfStack()
{
  int x=0;
  return top_of_stack - (size_t) &x;
} 

コードがマルチスレッドの場合、スレッドごとに top_of_stack 変数を格納する必要があります。

于 2008-09-10T12:02:00.633 に答える
9

コンパイラがstackavail()をサポートしているかどうかを確認してください

于 2008-09-10T13:40:50.003 に答える
6

スタック全体のサイズがわかっていると仮定すると、アセンブリ コードを追加して ESP を読み取ることができます。
ESP を読み取ってメイン関数に保存すると、現在の ESP とメインにある ESP を比較して、ESP がどれだけ変化したかを確認できます。これにより、使用されているスタックの量がわかります。

于 2008-09-10T12:01:09.433 に答える
5

これは私が諦めた問題です。多くのハッキングと(ほとんど)祈ることで、特定のマシンで特定の時間に機能するソリューションを得ることができます。しかし、一般的に、これを行うための適切な方法はないようです。

プログラムの外部からスタックの位置とサイズを取得する必要があります(Linuxでは取得する場合があります/proc/<pid>/maps)。プログラムでは、スタックのどこにいるかを何らかの方法でテストする必要があります。ローカル変数を使用することは可能ですが、それらが実際にスタック上にあるという実際の保証はありません。いくつかのアセンブリを使用して、スタックポインタレジスタから値を取得することもできます。

これで、スタックの場所、サイズ、および現在の位置がわかり、スタックがどの方向に成長するかがわかっていると想定します。いつスタックオーバーフローモードになりますか?見積もり(つまり、ローカル変数のアドレスまたはスタックポインターからの値)はおそらく少し楽観的すぎるため、最後の方でそれを行わない方がよいでしょう。スタックポインタを超えてメモリをアドレス指定することは珍しいことではありません。また、特定の関数(およびそれが呼び出す関数)がスタック上にどれだけのスペースを必要とするかについての手がかりはありません。したがって、最後にかなりのスペースを残す必要があります。

私はあなたがこの混乱に入らないようにアドバイスし、非常に深い再帰を避けようとすることしかできません。スタックサイズを増やすこともできます。Windowsでは、これを実行可能ファイルにコンパイルする必要があると思います。

于 2008-09-10T12:47:35.267 に答える
4

おそらくこれは Windows プラットフォームでのみ役立つでしょう:

exe の PE ヘッダー (IMAGE_NT_HEADERS) には、次のようなレコードがあります。

typedef構造体_IMAGE_NT_HEADERS {
    DWORD 署名;
    IMAGE_FILE_HEADER ファイルヘッダー;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

typedef構造体_IMAGE_OPTIONAL_HEADER {
    ...
    DWORD SizeOfStackReserve;
    DWORD SizeOfStackCommit;
    ...
}

これらの値を取得する簡単な方法があります: GetModuleHandle(NULL) を使用すると、モジュールのイメージベース (ハンドル)、IMAGE_NT_HEADERS 構造 (imagebase+IMAGE_DOS_HEADER. e_lfanew) -> IMAGE_NT_HEADERS で、そこに次のフィールドがあります: SizeOfStackReserveSizeOfStackCommit

OS がスタックに割り当てるスペースの最大量は、SizeOfStackReserve です。

これを試すことを検討している場合は、お知らせください。サポートさせていただきます。ある時点で使用されているスタックのサイズを取得する方法があります。

于 2008-09-11T13:55:22.880 に答える
3

Raymond Chen(The Old New Thing)は、この種の質問に対する良い答えを持っています。

あなたが尋ねなければならないなら、あなたはおそらく何か間違ったことをしているでしょう。

スタック割り当てに関するWin32の詳細は次のとおりです:MSDN

スタックスペースによって制限される可能性があると思われる場合は、ほぼ確実に使用可能な仮想メモリによって制限されます。その場合は、別の解決策を見つける必要があります。

正確に何をしようとしていますか?

于 2008-09-10T12:38:18.407 に答える
3

Windowsの場合:Kernel32.dllのVirtualQuery関数を使用する前にこれを実行しました。C#の例しかありませんが、テクニックを示しています。

public static class StackManagement
    {
        [StructLayout(LayoutKind.Sequential)]
        struct MEMORY_BASIC_INFORMATION
        {
            public UIntPtr BaseAddress;
            public UIntPtr AllocationBase;
            public uint AllocationProtect;
            public UIntPtr RegionSize;
            public uint State;
            public uint Protect;
            public uint Type;
        };

        private const long STACK_RESERVED_SPACE = 4096 * 16;

        public unsafe static bool CheckForSufficientStack(UInt64 bytes)
        {
            MEMORY_BASIC_INFORMATION stackInfo = new MEMORY_BASIC_INFORMATION();
            UIntPtr currentAddr = new UIntPtr(&stackInfo);
            VirtualQuery(currentAddr, ref stackInfo, sizeof(MEMORY_BASIC_INFORMATION));

            UInt64 stackBytesLeft = currentAddr.ToUInt64() - stackInfo.AllocationBase.ToUInt64();

            return stackBytesLeft > (bytes + STACK_RESERVED_SPACE);
        }

        [DllImport("kernel32.dll")]
        private static extern int VirtualQuery(UIntPtr lpAddress, ref MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);
    }

ところで:このコードは、コードのバグを修正しようとしたときに私が尋ねた別の質問のStackOverflowでも見つけることができます:算術演算により、安全でないC#でオーバーフローが発生しましたここにリンクの説明を入力してください

于 2012-01-03T17:44:37.270 に答える
2

Linuxでは、getrusageを呼び出して、返されたstruct rusageのru_isrssメンバー(整数の非共有スタックサイズ)を確認します。

MINGWサイトとそのsourceforgeサイトのパッチの追跡から、2008年5月にgetrusageの周りでパッチが適用され、かなり長い間一般的にサポートされているように見えます。MinGWでサポートされている一般的なLinux機能の量に関する注意事項を注意深く確認する必要があります。

于 2008-09-12T17:34:02.097 に答える