Limit des Stapels

10

Kürzlich habe ich das Limit eines Stacks auf drei Geräten mit unterschiedlichen Betriebssystemen getestet (mit Limit meine ich die maximale Anzahl von Levels, die der Stack haben kann), und ich habe festgestellt, dass es mir jedes Mal, wenn ich 2 ^ 16 Levels treffe, gibt Überlauffehler, und wenn ich 2 ^ 16-1 setze, funktioniert es richtig.

Meine Frage ist also - ist das wahr? Hat der Stack per Definition das maximale Limit 2 ^ 16-1 oder hängt es vom Betriebssystem ab?

Anonymus
quelle
5
here i mean by limit the maximum number of levels that can the stack haveWas ist ein Level?
Tkausl
3
Wie testest du es? Verwenden Sie ein 2-Byte (dword) als Eingabe?
Pro3Carp3
6
Welche Programmiersprache verwenden Sie? Eine solch scharfe Begrenzung bei einer festen Anzahl zeigt an, dass es sich um eine absichtliche Begrenzung durch Ihre sprachspezifische Laufzeit handelt und nicht durch die Stapelgröße, die das Betriebssystem zugewiesen hat. Zum Beispiel scheint Python standardmäßig ein Limit von 1000 zu erzwingen, um zu verhindern, dass Sie einen echten Stapelüberlauf erreichen (die Wiederherstellung nach einem echten Stapelüberlauf ist schwierig)
CodesInChaos

Antworten:

20

Es ist stark betriebssystemspezifisch (und rechnerspezifisch) und unter einigen Betriebssystemen haben Sie einige Möglichkeiten, das Limit zu konfigurieren (und sogar zu erhöhen). Es ist sogar compilerspezifisch (oder spezifisch für Ihre Programmiersprachenimplementierung), da einige Compiler (einschließlich des aktuellen GCC für eine begrenzte Art von C-Code) einige Tail-Aufrufe optimieren können .

(Einige Programmiersprachen-Spezifikationen erfordern Tail-Call-Optimierungen, z. B. R5RS. )

Ich bin mir nicht sicher, ob Ihre Frage Sinn macht (und schon gar nicht Ihr 2 16- Limit). Auf meinem Linux-Desktop (Debian / Sid / x86-64, Linux 4.9-Kernel, 32 GB RAM, Intel i5-4690S) habe ich möglicherweise einen Aufrufstapel von bis zu 8 Megabyte (und ich könnte dieses Limit erhöhen, wenn ich es wirklich wollte ).

Multithreading und ASLR machen Ihre Frage viel komplexer . Siehe z. B. pthread_attr_setstack (3) . Lesen Sie auch über Split-Stacks (häufig von Go- Implementierungen verwendet) und über den Continuation-Passing-Stil . Siehe auch diese Antwort.

Für das, was es wert ist, habe ich gerade den folgenden C99 (und auch C11) Code ausprobiert:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void recfun(int x, int d) {
  printf("start recfun x=%d d=%d\n", x, d);
  fflush(NULL);
  if (d>0)
    recfun(x+1, d-1);
  printf("end recfun x=%d d=%d\n", x, d);
}

int main(int argc, char**argv) {
  int md = argc>1?atoi(argv[1]):10000;
  printf ("start md=%d\n", md);
  recfun(0, md);
  printf("end md=%d clock=%ld µs\n", md, clock());
}    
// eof recur.c

und ich konnte dieses recurProgramm (kompiliert mit GCC 6 as gcc -Wall -O recur.c -o recur) ausführen recur 161000 (so weit über Ihrem 2 16- Limit). Damit hat recur 256000es auch geklappt. Damit recur 456000stürzte es ab (mit einem Stapelüberlauf für Level x=272057). Ich habe nicht die Geduld für andere Tests. Versuchen Sie das auf Ihrem Computer. Vergessen Sie nicht, nach Optimierungen zu fragen.

Als Faustregel (für Desktops, Laptops, Tablets) gilt möglicherweise, dass Ihr Anrufstapel unter einem Megabyte liegt.

Wenn ich auch -fstack-usage an übergebe , erhalte gccich die folgende recur.suDatei (Zahlen sind in Bytes, was mit meiner 8-MB-Stapelbegrenzungsintuition übereinstimmt; vergessen Sie nicht den Aufrufrahmen mainund vor allem das anfängliche Stapellayout, das der Kernel beim Ausführen installiert hat (2) ) ..., für crt0 ):

 recur.c:5:10:recfun    32  static
 recur.c:13:9:main  16  static

PS. Mein Arduino hat einen Atmega328 mit nur 2 KB RAM, kann also sicher nicht so viel wiederholen. Ich denke, auf Arduinos sind höchstens ein paar Hundert Stack-Frames praktisch möglich.

Basile Starynkevitch
quelle
3

Die Stapelgröße für den Hauptthread eines Windows-Prozesses wird vom Linker festgelegt. Der Standardwert ist 1 MB, kann jedoch mit dem Schalter / STACK eingestellt werden. Später erstellte Threads können den Parameter dwStackSize der Funktion CreateThread verwenden.

Wenn Sie also verschiedene Windows-Betriebssysteme testen, haben alle seit mindestens NT4.0 dieselbe Standardstapelgröße.

Eric
quelle