En plus des autres réponses, j'aimerais ajouter que lors de la création de RAM entre la pile et l'espace de tas, vous devez également tenir compte de l'espace pour les données statiques non constantes (par exemple, les fichiers globaux, les fonctions statiques et à l'échelle du programme). globales du point de vue C, et probablement d'autres pour C ++).
Fonctionnement de l'allocation de pile / segment de mémoire
Il convient de noter que le fichier d'assemblage de démarrage est un moyen de définir la région; la chaîne d'outils (à la fois votre environnement de génération et votre environnement d'exécution) se soucie principalement des symboles qui définissent le début de stackspace (utilisé pour stocker le pointeur de pile initial dans la table vectorielle) et le début et la fin de l'espace de tas (utilisé par la dynamique allocateur de mémoire, généralement fourni par votre libc)
Dans l'exemple d'OP, seuls 2 symboles sont définis, une taille de pile à 1 ko et une taille de tas à 0 b. Ces valeurs sont utilisées ailleurs pour produire réellement les espaces de pile et de tas
Dans l'exemple @Gilles, les tailles sont définies et utilisées dans le fichier d'assemblage pour définir un espace de pile commençant n'importe où et durant la taille, identifié par le symbole Stack_Mem et définissant une étiquette __initial_sp à la fin. De même pour le tas, où l'espace est le symbole Heap_Mem (0,5 ko en taille), mais avec des étiquettes au début et à la fin (__heap_base et __heap_limit).
Ceux-ci sont traités par l'éditeur de liens, qui n'allouera rien dans l'espace de pile et l'espace de tas car cette mémoire est occupée (par les symboles Stack_Mem et Heap_Mem), mais il peut placer ces mémoires et tous les globaux où il a besoin. Les étiquettes finissent par être des symboles sans longueur aux adresses données. Le __initial_sp est utilisé directement pour la table vectorielle au moment du lien, et le __heap_base et __heap_limit par votre code d'exécution. Les adresses réelles des symboles sont attribuées par l'éditeur de liens en fonction de leur emplacement.
Comme je l'ai mentionné ci-dessus, ces symboles ne doivent pas réellement provenir d'un fichier startup.s. Ils peuvent provenir de la configuration de votre éditeur de liens (fichier Scatter Load dans Keil, linkerscript dans GNU), et dans ceux-ci, vous pouvez avoir un contrôle plus fin sur le placement. Par exemple, vous pouvez forcer la pile au début ou à la fin de la RAM, ou garder vos globaux à l'écart du tas ou de tout ce que vous voulez. Vous pouvez même spécifier que le HEAP ou la STACK n'occupent que la RAM restante après le placement des globaux. NOTEZ cependant que vous devez être prudent en ajoutant plus de variables statiques que votre autre mémoire diminuera.
Cependant, chaque chaîne d'outils est différente et la façon d'écrire le fichier de configuration et les symboles que votre allocateur de mémoire dynamique utilisera devront provenir de la documentation de votre environnement particulier.
Dimensionnement de la pile
En ce qui concerne la façon de déterminer la taille de la pile, de nombreuses chaînes d'outils peuvent vous donner une profondeur de pile maximale en analysant les arborescences d'appels de fonction de votre programme, SI vous n'utilisez pas de récursivité ou des pointeurs de fonction. Si vous les utilisez, estimez une taille de pile et pré-remplissez-la avec des valeurs cardinales (peut-être via la fonction d'entrée avant main), puis vérifiez après que votre programme s'est exécuté pendant un certain temps où la profondeur maximale était (qui est où les valeurs cardinales fin). Si vous avez pleinement exercé votre programme à ses limites, vous saurez assez précisément si vous pouvez réduire la pile ou, si votre programme se bloque ou s'il ne reste aucune valeur cardinale, que vous devez augmenter la pile et réessayer.
Dimensionnement du tas
La détermination de la taille du segment de mémoire dépend un peu plus de l'application. Si vous ne faites que l'allocation dynamique au démarrage, vous pouvez simplement ajouter l'espace requis dans votre code de démarrage (plus une surcharge pour la gestion de la mémoire). Si vous avez accès à la source de votre gestionnaire de mémoire, vous pouvez savoir exactement ce qu'est la surcharge, et peut-être même écrire du code pour parcourir la mémoire et vous donner des informations d'utilisation. Pour les applications qui ont besoin de mémoire d'exécution dynamique (par exemple l'allocation de tampons pour les trames Ethernet entrantes), le mieux que je puisse suggérer est d'affiner soigneusement votre taille de pile et de donner au tas tout ce qui reste après la pile et la statique.
Note finale (RTOS)
La question d'OP a été étiquetée pour le métal nu, mais je veux ajouter une note pour les RTOS. Souvent (toujours?) Chaque tâche / processus / thread (je vais simplement écrire la tâche ici pour plus de simplicité) se verra attribuer une taille de pile lors de la création de la tâche, en plus des piles de tâches, il y aura probablement un petit système d'exploitation pile (utilisée pour les interruptions et autres)
Les structures de comptabilité des tâches et les piles doivent être allouées quelque part, et ce sera souvent à partir de l'espace de tas global de votre application. Dans ces cas, la taille initiale de votre pile n'a souvent pas d'importance, car le système d'exploitation ne l'utilisera que lors de l'initialisation. J'ai vu, par exemple, spécifier TOUS les espaces restants pendant la liaison alloués au HEAP et placer le pointeur de pile initial à la fin du tas pour grandir dans le tas, sachant que le système d'exploitation allouera à partir du début du tas et allouera la pile du système d'exploitation juste avant d'abandonner la pile initial_sp. Ensuite, tout l'espace est utilisé pour allouer des piles de tâches et d'autres mémoires allouées dynamiquement.