Limite de la pile

10

Récemment, j'ai testé la limite d'une pile sur trois appareils avec des systèmes d'exploitation différents (par limite, je veux dire le nombre maximal de niveaux que la pile peut avoir), et j'ai remarqué que chaque fois que je touche 2 ^ 16 niveaux, cela me donne erreur de débordement, et quand je mets 2 ^ 16-1 cela fonctionne correctement.

Ma question est donc - est-ce vrai? La pile a-t-elle par définition la limite maximale 2 ^ 16-1 ou cela dépend du système d'exploitation?

Anonymus
la source
5
here i mean by limit the maximum number of levels that can the stack havequel est un niveau?
tkausl
3
Comment le testez-vous? Utilisez-vous une entrée de 2 octets (dword)?
pro3carp3
6
Quel langage de programmation utilisez-vous? Une telle limite précise à un nombre fixe indique que c'est une limitation délibérée par votre runtime spécifique à la langue et non par la taille de la pile allouée par le système d'exploitation. Par exemple, python semble appliquer une limite de 1000 par défaut pour vous empêcher de frapper un débordement de pile réel (la récupération à partir d'un débordement de pile réel est difficile)
CodesInChaos

Réponses:

20

Il est fortement spécifique au système d'exploitation (et spécifique à l'ordinateur) et sur certains systèmes d'exploitation, vous pouvez configurer (et même augmenter) la limite. Il est même spécifique au compilateur (ou à l'implémentation de votre langage de programmation), car certains compilateurs (y compris le GCC récent pour un type limité de code C) sont capables d'optimiser certains appels de queue .

(certaines spécifications de langage de programmation nécessitent des optimisations d'appel de fin, par exemple R5RS )

Je ne suis pas sûr que votre question ait du sens (et certainement pas votre limite de 2 16 ). Sur mon bureau Linux (Debian / Sid / x86-64, noyau Linux 4.9, 32 Go de RAM, Intel i5-4690S), je pourrais avoir une pile d'appels allant jusqu'à 8 mégaoctets (et je pourrais augmenter cette limite, si je le voulais vraiment) ).

Le multi-threading et l' ASLR rendent votre question beaucoup plus complexe . Voir par exemple pthread_attr_setstack (3) . Lisez également sur les piles fractionnées (souvent utilisées par les implémentations Go ) et sur le style de passage de continuation . Voir aussi cette réponse.

Pour ce que ça vaut, j'ai juste essayé le code C99 (et aussi C11) suivant:

#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

et j'ai pu exécuter ce recurprogramme (compilé avec GCC 6 as gcc -Wall -O recur.c -o recur) en tant que recur 161000 (bien au-dessus de votre limite de 2 16 ). Avec recur 256000cela a également fonctionné. Avec recur 456000elle s'est écrasée (avec un débordement de pile pour le niveau x=272057). Je n'ai pas la patience pour d'autres tests. Essayez cela sur votre ordinateur. N'oubliez pas de demander des optimisations.

Une règle de base (pour les ordinateurs de bureau, les ordinateurs portables, les tablettes) peut être de maintenir votre pile d'appels en dessous d'un mégaoctet.

En passant également -fstack-usage à J'obtiens gccle recur.sufichier suivant (les nombres sont en octets, cohérents avec mon intuition de limite de pile de 8 Mo; n'oubliez pas le maincadre d'appel, et surtout la disposition de pile initiale, installée par le noyau lors de l' exécution de execve (2 ) ..., pour crt0 ):

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

PS. Mon Arduino a un Atmega328 avec seulement 2 Ko de RAM, donc ne peut certainement pas se reproduire autant. Je suppose que seulement quelques centaines de cadres de pile sont tout au plus pratiquement possibles sur Arduinos.

Basile Starynkevitch
la source
3

La taille de la pile pour le thread principal d'un processus Windows est définie par l'éditeur de liens. La valeur par défaut est 1 Mo mais peut être ajustée avec le commutateur / STACK. Les threads créés ultérieurement peuvent utiliser le paramètre dwStackSize de la fonction CreateThread.

Donc .. si vous testez différents systèmes d'exploitation Windows, ils ont tous la même taille de pile par défaut depuis au moins NT4.0.

Eric
la source