web-dev-qa-db-fra.com

Pourquoi la taille de la pile en C # correspond-elle à 1 Mo exactement?

Les PC actuels ont une grande quantité de mémoire physique RAM), mais la taille de la pile de C # n’est que de 1 Mo pour les processus 32 bits et de 4 Mo pour les processus 64 bits ( capacité de la pile). en C # ).

Pourquoi la taille de la pile dans CLR est-elle toujours aussi limitée?

Et pourquoi est-ce exactement 1 Mo (4 Mo) (et non 2 Mo ou 512 Ko)? Pourquoi a-t-on décidé d'utiliser ces montants?

Je suis intéressé par les considérations et les raisons qui ont motivé cette décision .

84
Nikolay Kostov

enter image description here

Vous regardez le gars qui a fait ce choix. David Cutler et son équipe ont sélectionné un mégaoctet comme taille de pile par défaut. Rien à voir avec .NET ou C #, cela a été cloué lors de la création de Windows NT. Un mégaoctet correspond à ce qu'il choisit lorsque l'en-tête EXE d'un programme ou l'appel de CreateThread () Winapi ne spécifie pas explicitement la taille de la pile. Comme d'habitude, presque tous les programmeurs laissent la taille à l'OS.

Ce choix est probablement antérieur à la conception de Windows NT, l’histoire est bien trop floue à ce sujet. Ce serait bien si Cutler écrivait un livre à ce sujet, mais il n'a jamais été écrivain. Il a eu une influence extraordinaire sur le fonctionnement des ordinateurs. Son premier système d'exploitation était RSX-11M, un système d'exploitation 16 bits pour les ordinateurs DEC (Digital Equipment Corporation). Il a fortement influencé le CP/M de Gary Kildall, le premier système d'exploitation décent pour les microprocesseurs 8 bits. Ce qui a fortement influencé MS-DOS.

Sa conception suivante était VMS, un système d’exploitation pour processeurs 32 bits prenant en charge la mémoire virtuelle. Connait un grand succès. Son prochain contrat a été annulé par DEC au moment où la société a commencé à se désintégrer, incapable de concurrencer le matériel informatique bon marché. Cue Microsoft, ils lui ont fait une offre qu’il ne pouvait refuser. Beaucoup de ses collègues ont également rejoint. Ils ont travaillé sur VMS v2, plus connu sous le nom de Windows NT. DEC s’en est fâché, l’argent a changé de mains pour le régler. Que je ne sache pas si VMS a déjà choisi un mégaoctet, je ne connais que suffisamment le RSX-11. Ce n'est pas improbable.

Assez d'histoire. Un mégaoctet est un lot , un vrai thread consomme rarement plus de quelques poignées de kilo-octets. Donc, un mégaoctet est en fait plutôt inutile. C’est toutefois le type de gaspillage que vous pouvez vous permettre avec un système d’exploitation de mémoire virtuelle paginée à la demande, ce mégaoctet n’est que mémoire virtuelle. Juste des nombres au processeur, un pour chaque 4096 octets. Vous n'utilisez jamais réellement la mémoire physique, la RAM de la machine), jusqu'à ce que vous l'adressiez réellement.

C’est excessif dans un programme .NET car la taille d’un mégaoctet a été choisie à l’origine pour les programmes natifs. Qui ont tendance à créer de grands cadres de pile, stockant des chaînes et des tampons (matrices) sur la pile également. Tristement célèbre pour être un vecteur d’attaque de logiciels malveillants, un débordement de mémoire tampon peut manipuler le programme avec des données. Ce n'est pas la façon dont les programmes .NET fonctionnent, les chaînes et les tableaux sont alloués sur le tas du GC et l'indexation est vérifiée. Le seul moyen d'allouer de l'espace sur la pile avec C # est d'utiliser le mot clé unsafe stackalloc.

La gigue est la seule utilisation non triviale de la pile dans .NET. Il utilise la pile de votre thread pour compiler juste-à-temps MSIL en code machine. Je n'ai jamais vu ni vérifié l'espace requis, cela dépend de la nature du code et de l'activation ou non de l'optimiseur, mais quelques dizaines de kilo-octets suffisent. Sinon, comment ce site Web tire-t-il son nom, un débordement de pile dans un programme .NET est assez fatal. Il ne reste plus assez d'espace libre (moins de 3 kilo-octets) pour toujours enregistrer de manière fiable tout code qui tente d'attraper l'exception. Kaboom to desktop est la seule option.

Dernier point mais non le moindre, un programme .NET fait quelque chose d'assez improductif avec la pile. Le CLR commit la pile d'un thread. C'est un mot cher qui signifie qu'il ne se contente pas de réserver la taille de la pile, il s'assure également que cet espace est réservé dans le fichier de pagination du système d'exploitation afin que la pile puisse toujours être remplacée lorsque nécessaire. Ne pas commettre est une erreur fatale et termine un programme sans condition. Cela ne se produit que sur une machine avec très peu de RAM qui exécute un trop grand nombre de processus, une telle machine se sera transformée en mélasse avant que les programmes ne commencent à disparaître. Un problème possible il y a plus de 15 ans, pas aujourd'hui. Programmeurs leur programme agissant comme une voiture de course F1 utilise le <disableCommitThreadStack> élément dans leur fichier .config.

Fwiw, Cutler n’a pas arrêté de concevoir des systèmes d’exploitation. Cette photo a été faite pendant qu'il travaillait sur Azure.


Mise à jour, j'ai remarqué que .NET n'engage plus la pile. Je ne sais pas exactement quand ni pourquoi cela s'est produit, cela fait trop longtemps que j'ai vérifié. Je suppose que ce changement de conception s’est produit quelque part autour de .NET 4.5. Changement assez sensible.

179
Hans Passant

La taille de la pile réservée par défaut est spécifiée par l'éditeur de liens et elle peut être remplacée par les développeurs en modifiant la valeur de PE au moment de la liaison ou par un thread individuel en spécifiant le paramètre dwStackSize pour le CreateThread WinAPI. une fonction.

Si vous créez un thread avec une taille de pile initiale supérieure ou égale à la taille de pile par défaut, il est arrondi au multiple de 1 Mo le plus proche.

Pourquoi la valeur est égale à 1 Mo pour les processus 32 bits et 4 Mo pour 64 bits? Je pense que vous devriez demander aux développeurs qui ont conçu Windows ou attendre que l'un d'entre eux réponde à votre question.

Probablement Mark Russinovich le sait et vous pouvez le contacter . Vous pouvez peut-être trouver cette information dans ses livres Windows Internals plus tôt que la sixième édition, qui décrit moins d’informations sur les piles plutôt que sur son article . Ou peut-être que Raymond Chen connaît les raisons puisqu'il écrit des choses intéressantes sur les internes de Windows et son histoire. Il peut également répondre à votre question, mais vous devez poster une suggestion à l'adresse Boîte à suggestions .

Mais à ce stade, je vais essayer d’expliquer certaines des raisons probables pour lesquelles Microsoft a choisi ces valeurs à l’aide des blogs MSDN, Mark's et Raymond.

Les valeurs par défaut ont probablement ces valeurs car, à une époque récente, les PC étaient lents et que l’allocation de mémoire sur la pile était beaucoup plus rapide que celle allouée dans le tas. Et comme les allocations de pile étaient beaucoup moins chères, elles étaient utilisées, mais cela nécessitait une plus grande taille de pile.

La valeur correspondait donc à la taille de pile réservée optimale pour la plupart des applications. C'est optimal car permet de faire beaucoup d'appels imbriqués et d'allouer de la mémoire sur la pile pour passer des structures aux fonctions d'appel. En même temps, cela permet de créer beaucoup de threads.

De nos jours, ces valeurs sont principalement utilisées pour la compatibilité ascendante, car les structures transmises en tant que paramètres aux fonctions WinAPI sont toujours allouées sur la pile. Mais si vous n'utilisez pas d'allocation de pile, l'utilisation de la pile par un thread sera nettement inférieure à la valeur par défaut de 1 Mo. Cette utilisation est inutile, comme l'a mentionné Hans Passant. Et pour éviter cela, le système d'exploitation valide uniquement la première page de la pile (4 Ko), si aucun autre élément n'est spécifié dans l'en-tête PE de l'application. Les autres pages sont allouées sur demande.

Certaines applications remplacent l'espace d'adressage réservé et sont initialement destinées à optimiser l'utilisation de la mémoire. Par exemple, la taille maximale de la pile d'un IIS du processus natif est de 256 Ko ( KB932909 ). Et la diminution des valeurs par défaut est recommandé par Microsoft:

Il est préférable de choisir la taille de pile la plus petite possible et de valider la pile nécessaire au bon fonctionnement du thread ou de la fibre. Chaque page réservée à la pile ne peut être utilisée à aucune autre fin.

Sources:

  1. Taille de pile de threads (Microsoft Docs)
  2. Repousser les limites de Windows: processus et threads (Mark Russinovich)
  3. Par défaut, la taille de pile maximale d'un thread créé dans un processus natif IIS est de 256 Ko (KB932909)
3
Yoh Deadfall