Dans les ordinateurs, pour qu'un processus soit exécutable, il doit être placé en mémoire. Pour cela, un champ doit être affecté à un processus en mémoire. L'allocation de mémoire est un problème important dont il faut être conscient, en particulier dans les architectures de noyau et de système.

Examinons en détail l'allocation de mémoire Linux et comprenons ce qui se passe dans les coulisses.

Comment se fait l'allocation de mémoire ?

La plupart des ingénieurs en logiciel ne connaissent pas les détails de ce processus. Mais si vous êtes un candidat programmeur système, vous devriez en savoir plus à ce sujet. En regardant le processus d'allocation, il est nécessaire d'entrer dans un peu de détail sur Linux et le glibc bibliothèque.

Lorsque les applications ont besoin de mémoire, elles doivent en demander au système d'exploitation. Cette requête du noyau nécessitera naturellement un appel système. Vous ne pouvez pas allouer vous-même de la mémoire en mode utilisateur.

Le malloc() famille de fonctions est responsable de l'allocation de mémoire dans le langage C. La question à se poser ici est de savoir si malloc(), en tant que fonction glibc, effectue un appel système direct.

instagram viewer

Il n'y a pas d'appel système appelé malloc dans le noyau Linux. Cependant, il existe deux appels système pour les demandes de mémoire des applications, qui sont brk et mmap.

Étant donné que vous demanderez de la mémoire dans votre application via les fonctions de la glibc, vous vous demandez peut-être lequel de ces appels système la glibc utilise à ce stade. La réponse est les deux.

Le premier appel système: brk

Chaque processus a un champ de données contigu. Avec l'appel système brk, la valeur de rupture de programme, qui détermine la limite du champ de données, est augmentée et le processus d'allocation est exécuté.

Bien que l'allocation de mémoire avec cette méthode soit très rapide, il n'est pas toujours possible de restituer l'espace inutilisé au système.

Par exemple, supposons que vous allouez cinq champs, chacun d'une taille de 16 Ko, avec l'appel système brk via la fonction malloc(). Lorsque vous avez terminé avec le numéro deux de ces champs, il n'est pas possible de retourner la ressource pertinente (désallocation) afin que le système puisse l'utiliser. Parce que si vous réduisez la valeur de l'adresse pour montrer l'endroit où commence votre champ numéro deux, avec un appel à brk, vous aurez fait la désallocation pour les champs numéros trois, quatre et cinq.

Pour éviter la perte de mémoire dans ce scénario, l'implémentation de malloc dans la glibc surveille les emplacements alloués dans le champ de données de processus et spécifie ensuite de le renvoyer au système avec la fonction free(), afin que le système puisse utiliser l'espace libre pour plus de mémoire allocations.

En d'autres termes, après l'allocation de cinq zones de 16 Ko, si la deuxième zone est renvoyée avec la fonction free() et une autre zone de 16 Ko est à nouveau demandée après un certain temps, au lieu d'agrandir la zone de données via l'appel système brk, l'adresse précédente est renvoyée.

Cependant, si la nouvelle zone demandée est supérieure à 16 Ko, la zone de données sera agrandie en allouant une nouvelle zone avec l'appel système brk car la zone deux ne peut pas être utilisée. Bien que la zone numéro deux ne soit pas utilisée, l'application ne peut pas l'utiliser en raison de la différence de taille. En raison de scénarios comme celui-ci, il existe une situation appelée fragmentation interne et, en fait, vous pouvez rarement utiliser toutes les parties de la mémoire au maximum.

Pour une meilleure compréhension, essayez de compiler et d'exécuter l'exemple d'application suivant :

#inclure <stdio.h>
#inclure <stdlib.h>
#inclure <unistd.h>
entierprincipale(entier argc, carboniser*argv[])
{
carboniser *ptr[7];
entier n;
printf("PID de %s: %d", argv[0], getpid());
printf("Interruption initiale du programme: %p", sbrk (0));
pour (n=0; n<5; n++) ptr[n] = malloc (16 * 1024);
printf("Après 5 mallocs de 16 Ko: %p", sbrk (0));
libre(ptr[1]);
printf("Après la libération du deuxième 16 Ko: %p", sbrk (0));
ptr[5] = malloc (16 * 1024);
printf("Après avoir alloué le 6e de 16 Ko: %p", sbrk (0));
libre(ptr[5]);
printf("Après avoir libéré le dernier bloc: %p", sbrk (0));
ptr[6] = malloc (18 * 1024);
printf("Après avoir alloué un nouveau 18 Ko: %p", sbrk (0));
getchar();
retourner0;
}

Lorsque vous exécutez l'application, vous obtenez un résultat similaire à la sortie suivante :

PID de ./a.out: 31990
Programme initial Pause: 0x55ebcadf4000
Après 5 mallocs de 16 Ko: 0x55ebcadf4000
Après libération du deuxième 16 Ko: 0x55ebcadf4000
Après avoir alloué le 6ème de 16 Ko: 0x55ebcadf4000
Après avoir libéré le dernier bloc: 0x55ebcadf4000
Après avoir attribué un Nouveau18Ko: 0x55ebcadf4000

La sortie pour brk avec strace sera la suivante :

frein(NUL) = 0x5608595b6000
brk (0x5608595d7000) = 0x5608595d7000

Comme vous pouvez le voir, 0x21000 a été ajouté à l'adresse de fin du champ de données. Vous pouvez comprendre cela à partir de la valeur 0x5608595d7000. donc approximativement 0x21000, ou 132 Ko de mémoire ont été alloués.

Il y a deux points importants à considérer ici. Le premier est l'allocation de plus que le montant spécifié dans l'exemple de code. Une autre est la ligne de code qui a provoqué l'appel brk qui a fourni l'allocation.

Randomisation de la disposition de l'espace d'adressage: ASLR

Lorsque vous exécutez l'exemple d'application ci-dessus l'un après l'autre, vous verrez des valeurs d'adresse différentes à chaque fois. Faire changer l'espace d'adressage au hasard de cette façon complique considérablement le travail de attaques de sécurité et augmente la sécurité des logiciels.

Cependant, dans les architectures 32 bits, huit bits sont généralement utilisés pour randomiser l'espace d'adressage. Augmenter le nombre de bits ne sera pas approprié car la zone adressable sur les bits restants sera très faible. De plus, l'utilisation de combinaisons 8 bits uniquement ne rend pas les choses suffisamment difficiles pour l'attaquant.

Dans les architectures 64 bits, d'autre part, étant donné qu'il y a trop de bits qui peuvent être alloués pour le fonctionnement ASLR, un caractère aléatoire beaucoup plus important est fourni et le degré de sécurité augmente.

Le noyau Linux alimente également Appareils basés sur Android et la fonction ASLR est entièrement activée sur Android 4.0.3 et versions ultérieures. Même pour cette seule raison, il ne serait pas faux de dire qu'un smartphone 64 bits offre un avantage de sécurité significatif par rapport aux versions 32 bits.

En désactivant temporairement la fonctionnalité ASLR avec la commande suivante, il apparaîtra que l'application de test précédente renvoie les mêmes valeurs d'adresse à chaque exécution :

écho0 | sudo tee /proc/sys/kernel/randomize_va_space

Pour le remettre dans son état précédent, il suffira d'écrire 2 au lieu de 0 dans le même fichier.

Le deuxième appel système: mmap

mmap est le deuxième appel système utilisé pour l'allocation de mémoire sous Linux. Avec l'appel mmap, l'espace libre dans n'importe quelle zone de la mémoire est mappé sur l'espace d'adressage du processus appelant.

Dans une allocation de mémoire effectuée de cette manière, lorsque vous souhaitez retourner la deuxième partition de 16 Ko avec la fonction free() de l'exemple brk précédent, aucun mécanisme n'empêche cette opération. Le segment de mémoire concerné est supprimé de l'espace d'adressage du processus. Il est marqué comme n'étant plus utilisé et renvoyé au système.

Comme les allocations de mémoire avec mmap sont très lentes par rapport à celles avec brk, l'allocation brk est nécessaire.

Avec mmap, toute zone de mémoire libre est mappée sur l'espace d'adressage du processus, de sorte que le contenu de l'espace alloué est réinitialisé avant la fin de ce processus. Si la réinitialisation n'était pas effectuée de cette manière, les données appartenant au processus qui utilisait précédemment la zone de mémoire concernée pourraient également être accédées par le processus indépendant suivant. Il serait alors impossible de parler de sécurité dans les systèmes.

Importance de l'allocation de mémoire sous Linux

L'allocation de mémoire est très importante, en particulier pour les problèmes d'optimisation et de sécurité. Comme on le voit dans les exemples ci-dessus, ne pas bien comprendre ce problème peut signifier détruire la sécurité de votre système.

Même des concepts similaires à push et pop qui existent dans de nombreux langages de programmation sont basés sur des opérations d'allocation de mémoire. Être capable d'utiliser et de bien maîtriser la mémoire système est essentiel à la fois dans la programmation système embarquée et dans le développement d'une architecture système sécurisée et optimisée.

Si vous souhaitez également vous initier au développement du noyau Linux, envisagez d'abord de maîtriser le langage de programmation C.

Une brève introduction au langage de programmation C

Lire la suite

PartagerTweeterPartagerE-mail

Rubriques connexes

  • Linux
  • Mémoire d'ordinateur
  • Noyau Linux

A propos de l'auteur

Fatih Küçükkarakurt (7 articles publiés)

Un ingénieur et développeur de logiciels passionné de mathématiques et de technologie. Il a toujours aimé les ordinateurs, les mathématiques et la physique. Il a développé des projets de moteurs de jeux ainsi que des bibliothèques d'apprentissage automatique, de réseaux de neurones artificiels et d'algèbre linéaire. De plus continue à travailler sur l'apprentissage automatique et les matrices linéaires.

Plus de Fatih Küçükkarakurt

Abonnez-vous à notre newsletter

Rejoignez notre newsletter pour des conseils techniques, des critiques, des ebooks gratuits et des offres exclusives !

Cliquez ici pour vous abonner