Überprüfen der verfügbaren Stapelgröße in C

Lesezeit: 7 Minuten

Benutzeravatar von Paul Hargreaves
Paul Hargreaves

Ich verwende MinGW mit GCC 3.4.5 (mingw-special vista r3).

Meine C-Anwendung verwendet viel Stack, also habe ich mich gefragt, ob es eine Möglichkeit gibt, programmatisch zu sagen, wie viel Stack noch übrig ist, damit ich die Situation sauber handhaben kann, wenn ich feststelle, dass ich kurz davor bin, auszugehen.

Wenn nicht, auf welche andere Weise würden Sie das Problem umgehen, dass möglicherweise der Stapelspeicherplatz ausgeht?

Ich habe keine Ahnung, mit welcher Stapelgröße ich anfange, also müsste ich das auch programmatisch identifizieren.

Benutzeravatar von phoxis
Phoxie

Die Getrusage-Funktion liefert Ihnen die aktuelle Nutzung. (sehen man getrusage).

Das getrlimit unter Linux würde helfen, die Stapelgröße mit dem abzurufen RLIMIT_STACK Parameter.

#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);
}

Bitte werfen Sie einen Blick auf man getrlimit. Dieselben Informationen könnten von abgerufen werden ulimit -s oder ulimit -a Stapelgröße Zeile. Schau auch mal rein setrlimit Funktion, die es ermöglichen würde, die Grenzen festzulegen. Aber wie in den anderen Antworten erwähnt, sollten Sie Ihr Design wahrscheinlich überdenken, wenn Sie den Stapel anpassen müssen. Wenn Sie ein großes Array wollen, warum nehmen Sie den Speicher nicht aus dem Heap?

  • getrusage() funktioniert nicht für die Stapelgröße unter Linux. “ru_isrss (unmaintained) This field is currently unused on Linux.” (linux.die.net/man/2/getrusage). Ich weiß nicht, wann es dazu kam, aber es trifft auf Kernel 2.6.28 zu.

    – atz

    13. Januar 2012 um 11:46 Uhr

  • @phoxis:getrlimit (RLIMIT_STACK, &limit)scheint die Gesamtstapelgröße anzugeben, nicht die verbleibende freie Stapelgröße.

    – Benutzer2284570

    19. Juli 2016 um 17:32 Uhr

  • @ user2284570 : Von man getrlimit Ich kann sehen, dass es geschrieben steht: “Die maximale Größe des Prozessstapels in Bytes.” . Können Sie erläutern, warum Sie glauben, dass es die verbleibende Stapelgröße sein könnte?

    – phoxie

    21. Juli 2016 um 12:55 Uhr

  • @phoxis: Das sage ich. Es ist die Gesamtstapelgröße. Und im Fall des ᴏᴘ ist nur das Erhalten des Restes nützlich.

    – Benutzer2284570

    22. Juli 2016 um 13:20 Uhr

Es würde funktionieren, die Adresse einer lokalen Variablen vom Stack zu nehmen. Dann können Sie in einem stärker verschachtelten Aufruf die Adresse eines anderen Lokals subtrahieren, um den Unterschied zwischen ihnen zu finden

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;
} 

Wenn Ihr Code Multithreading ist, müssen Sie sich damit befassen, die top_of_stack-Variable pro Thread zu speichern.

  • Ich mag diese Antwort, aber ohne die Größe des Stapels im Voraus zu kennen, kann ich nicht sagen, ob ich ihn in die Luft jagen werde.

    – Paul Hargreaves

    10. September 2008 um 12:18 Uhr

  • Es gibt eine Standardgröße des Stacks für Threads auf Ihrem System. Unter Windows ist dies 1 MB Adressraum. Sie können dies steuern, wenn Sie Ihre eigenen Threads erstellen. Wie Skizz betont, wäre es jedoch am besten, sich keine Gedanken über das genaue Limit machen zu müssen!

    – Rob Walker

    10. September 2008 um 13:16 Uhr

  • Dies könnte insbesondere bei MinGW in Ordnung sein. Im Allgemeinen ist nicht garantiert, dass der Stack für ein Programm zusammenhängend ist. Es ist für eine Implementierung (beispielsweise eine, die keinen virtuellen Speicher hat) zulässig, Stack-Blöcke nach Bedarf zuzuweisen und sie miteinander zu verketten. Wenn Ihre Plattform dies tut, gibt es möglicherweise nicht einmal eine standardmäßige maximale Stapelgröße für ein Programm: Sie können einfach weitermachen, bis Ihnen der freie Speicher ausgeht. Aber ein guter Grund, trotzdem ein Limit zu haben, ist, zu verhindern, dass eine außer Kontrolle geratene Rekursion das gesamte System durch Erschöpfung des Speichers zum Erliegen bringt.

    – Steve Jessop

    2. November 2009 um 12:15 Uhr

  • Unter Linux erhalten Sie die Stapelgröße mit ulimit -a.

    – Mark Lakata

    29. Juni 2018 um 0:35 Uhr

  • Warnungen: Einige Plattformen (insbesondere eingebettete Systeme) ordnen keine Daten auf dem Stack zu (nur Funktionsrückgabeadressen werden auf dem Stack gespeichert). In diesem Fall ist die Adresse lokaler Variablen bedeutungslos.

    – Mark Lakata

    29. Juni 2018 um 0:37 Uhr

Prüfen Sie, ob Ihr Compiler stackavail() unterstützt

Angenommen, Sie kennen die Größe des vollständigen Stapels, könnten Sie wahrscheinlich einen Assembler-Code hinzufügen, um ESP zu lesen.
Wenn Sie ESP lesen und in der Hauptfunktion beiseite speichern, können Sie das aktuelle ESP mit dem ESP vergleichen, das Sie in der Hauptfunktion haben, und sehen, wie viel ESP sich geändert hat. Das gibt Ihnen einen Hinweis darauf, wie viel Stack Sie verwendet haben.

Das ist ein Problem, das ich aufgegeben habe. Mit viel Hacking und (meistens) Beten können Sie eine Lösung erhalten, die zu einem bestimmten Zeitpunkt auf einem bestimmten Computer funktioniert. Aber im Allgemeinen scheint es keinen anständigen Weg zu geben, dies zu tun.

Sie müssen die Stapelposition und -größe von außerhalb Ihres Programms erhalten (unter Linux erhalten Sie sie möglicherweise von /proc/<pid>/maps). In Ihrem Programm müssen Sie irgendwie testen, wo Sie sich am Stack befinden. Die Verwendung lokaler Variablen ist möglich, aber es gibt keine wirkliche Garantie, dass sie sich tatsächlich auf dem Stack befinden. Sie können auch versuchen, den Wert aus dem Stapelzeigerregister mit einer Assembly zu erhalten.

Jetzt haben Sie also den Ort des Stapels, seine Größe und die aktuelle Position und gehen davon aus, dass Sie wissen, in welche Richtung der Stapel wächst. Wann gehst du in den Stack-Overflow-Modus? Machen Sie es besser nicht ganz am Ende, weil Ihre Schätzung (dh Adresse der lokalen Variablen oder Wert vom Stapelzeiger) wahrscheinlich etwas zu optimistisch ist; Es ist nicht ungewöhnlich, Speicher jenseits des Stapelzeigers zu adressieren. Außerdem haben Sie keine Ahnung, wie viel Platz auf dem Stapel eine bestimmte Funktion (und die von ihr aufgerufenen Funktionen) benötigt. Man muss also am Ende ziemlich viel Platz lassen.

Ich kann Ihnen nur raten, sich nicht in diesen Schlamassel zu begeben und zu versuchen, sehr tiefe Rekursionen zu vermeiden. Vielleicht möchten Sie auch Ihre Stapelgröße erhöhen; Unter Windows müssen Sie dies in die ausführbare Datei kompilieren, glaube ich.

Benutzeravatar von botismarius
botismarius

Vielleicht hilft dies nur für die Windows-Plattform:

im PE-Header (IMAGE_NT_HEADERS) Ihrer exe gibt es einige Einträge wie:

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

typedef struct _IMAGE_OPTIONAL_HEADER {
    ...
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    ...
}

Es gibt eine einfache Möglichkeit, diese Werte zu erhalten: Die Verwendung von GetModuleHandle(NULL) gibt Ihnen die Imagebase (Handle) Ihres Moduls, Adresse, wo Sie eine IMAGE_DOS_HEADER-Struktur finden, die Ihnen hilft, die IMAGE_NT_HEADERS-Struktur zu finden (Imagebase+IMAGE_DOS_HEADER. e_lfanew) -> IMAGE_NT_HEADERS, und dort finden Sie diese Felder: SizeOfStackReserve und SizeOfStackCommit.

Die maximale Menge an Speicherplatz, die das Betriebssystem Ihrem Stack zuweist, ist SizeOfStackReserve.

Wenn Sie erwägen, dies zu versuchen, lassen Sie es mich wissen und ich werde Ihnen helfen. Es gibt eine Möglichkeit, die an einem bestimmten Punkt verwendete Stapelgröße zu erhalten.

Benutzeravatar von Tim Cooper
Tim Cooper

Raymond Chen (Das Alte Neue) hat eine gute Antwort auf diese Art von Frage:

Wenn Sie fragen müssen, machen Sie wahrscheinlich etwas falsch.

Hier sind einige Win32-Details zur Stapelzuweisung: MSDN.

Wenn Sie der Meinung sind, dass Sie möglicherweise durch den Stapelspeicher begrenzt sind, werden Sie mit ziemlicher Sicherheit durch den verfügbaren virtuellen Speicher eingeschränkt sein. In diesem Fall müssen Sie eine andere Lösung finden.

Was genau versuchst du zu tun?

  • Ein (nicht gutes) Beispiel wäre: void subroutine(int i) { char foo[20000]; i++; wenn (i < 1000) Unterprogramm (i); }

    – Paul Hargreaves

    10. September 2008 um 12:57 Uhr

  • Du hast Recht, das ist kein gutes Beispiel. Was ich wirklich wissen wollte, war, was Sie mit dem 20k-Array machen.

    – Schizz

    10. September 2008 um 13:39 Uhr

  • Wenn Sie jedoch jemals versucht haben, wirklich tragbaren Code zu schreiben, lernen Sie, dass “Sie immer fragen müssen und immer etwas falsch machen, weil es kein tragbares Konzept der “Stack-Nutzung” gibt und es doch das des Programmierers ist Verantwortung, nicht zu viel Stack zu verwenden. Es ist also am besten, sich einfach der Verschwörung des Schweigens anzuschließen, einen Funktionstest zu schreiben, von dem Sie hoffen, dass er so viel Stack verbraucht, wie Ihr Programm jemals in der Praxis verbraucht, und es dem Plattformintegrator zu überlassen, sich darum zu kümmern”.

    – Steve Jessop

    2. November 2009 um 12:10 Uhr

  • Die Frage ist nicht “Soll ich die Stapelgröße überprüfen?” es ist “Wie überprüfe ich die Stapelgröße?”

    – Gerechtigkeit

    20. Mai 2018 um 18:25 Uhr

  • Ich möchte nur die Stapelgröße abfragen, um die Frage zu beantworten: “Werden dateibezogene Arrays auf den Stapel gelegt?” Ich nehme an, nein, aber indem ich zwei einfache Programme mache, die die Stapelgröße abfragen, eines mit und eines ohne ein dateibezogenes Array, kann ich die Frage selbst beantworten.

    – KANJICODER

    17. April 2020 um 21:49 Uhr

1403300cookie-checkÜberprüfen der verfügbaren Stapelgröße in C

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy