Maison > Article > développement back-end > Compréhension approfondie de la gestion de la mémoire Nginx (photo)
Ce que cet article vous apporte, c'est une compréhension approfondie de la gestion de la mémoire de Nginx (images). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.
1. Présentation
La mémoire de l'application peut être simplement divisée en mémoire tas et mémoire pile. Pour la mémoire de pile, lorsqu'une fonction est compilée, le compilateur insère du code qui déplace la position actuelle du pointeur de la pile pour réaliser l'autogestion de l'espace de la pile. Pour la mémoire tas, les programmeurs doivent généralement la gérer. La gestion de la mémoire dont nous parlons habituellement n'est que la gestion de la mémoire de l'espace tas.
Pour la mémoire, notre utilisation peut être simplifiée en 3 étapes, demander de la mémoire, utiliser de la mémoire et libérer de la mémoire. La demande de mémoire et l'utilisation de la mémoire nécessitent généralement des opérations explicites de la part des programmeurs, mais la libération de la mémoire ne nécessite pas nécessairement des opérations explicites de la part des programmeurs. Actuellement, de nombreux langages de haut niveau fournissent des mécanismes de récupération de place et vous pouvez choisir le moment où libérer la mémoire. Exemple : Go et Java ont implémenté le garbage collection. Recycling, le langage C n'a pas encore implémenté le garbage collection. En C++, le garbage collection peut être réalisé via des pointeurs intelligents.
En plus de la gestion de la mémoire au niveau du langage, nous devons parfois gérer la mémoire par nous-mêmes dans le programme. De manière générale, pour la gestion de la mémoire, je pense qu'il s'agit principalement de résoudre les problèmes suivants. :
Lorsqu'un utilisateur demande de la mémoire, comment trouver rapidement le bloc mémoire qui répond aux besoins de l'utilisateur ?
Comment éviter la fragmentation de la mémoire lorsque les utilisateurs libèrent de la mémoire ?
Qu'il s'agisse d'une gestion de la mémoire implémentée au niveau du langage ou d'une gestion de la mémoire implémentée par l'application elle-même, la plupart de la mémoire est divisée en plusieurs types selon leur taille, chacun utilisant un mode de gestion différent. Une classification courante consiste à diviser différents types de mémoire en puissances entières de 2 via des listes chaînées. Lors de l'interrogation, effectuez une recherche dans des listes chaînées de tailles correspondantes. Si vous ne le trouvez pas, vous pouvez envisager de prendre un morceau d'un bloc de mémoire plus grand. en le divisant en plusieurs petits points de mémoire. Bien entendu, pour les mémoires particulièrement volumineuses, la gestion de la mémoire au niveau du langage peut appeler directement les appels système liés à la gestion de la mémoire, et la gestion de la mémoire au niveau de l'application peut utiliser directement la gestion de la mémoire au niveau du langage.
La gestion de la mémoire nginx dans son ensemble peut être divisée en 2 parties,
La première partie est la mémoire conventionnelle pool, avec La gestion de la mémoire requise par le processus;
La deuxième partie est la gestion de la mémoire partagée. Dans l’ensemble, la mémoire partagée est beaucoup plus complexe que les pools de mémoire.
2. Gestion du pool de mémoire nginx
2.1 Description
La version de nginx utilisée dans cette partie est 1.15.3
Pour le code source spécifique, veuillez vous référer au fichier src/core/ngx_palloc.c
2.2 Implémentation de nginx
2.2.1 Processus d'utilisation
L'utilisation du pool de mémoire nginx est relativement simple et peut être divisée en 3 étapes
<.>//size代表ngx_pool_t一块的大小 ngx_pool_t* ngx_create_pool(size_t size, ngx_log_t *log)
//从pool中申请size大小的内存 void* ngx_palloc(ngx_pool_t *pool, size_t size)
//释放从pool中申请的大块内存 ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p) //释放整个内存池 void ngx_destroy_pool(ngx_pool_t *pool)2.2.2 Implémentation spécifique
//创建内存池的参数size减去头部管理结构ngx_pool_t的大小 pool->max = size - sizeof(ngx_pool_t);Pour un petit espace mémoire, nginx vérifie d'abord si l'espace à allouer dans le bloc mémoire actuel peut répondre aux besoins de l'utilisateur. Si tel est le cas, alors cette partie de la mémoire est renvoyée directement. Si les besoins de l'utilisateur ne peuvent pas être satisfaits, vous devez demander à nouveau un bloc de mémoire. Le bloc de mémoire demandé a la même taille que l'espace de bloc actuel. Le bloc de mémoire nouvellement appliqué est lié au bloc de mémoire précédent via un. liste chaînée, et le bloc de mémoire requis par l'utilisateur est alloué à partir du nouveau bloc de mémoire Mémoire. Une petite mémoire n'est pas libérée. L'utilisateur peut l'utiliser directement après l'application. Même si elle n'est plus utilisée plus tard, il n'est pas nécessaire de libérer la mémoire. Étant donné que les utilisateurs ne savent parfois pas si le bloc de mémoire qu'ils utilisent est grand ou petit, ils peuvent également appeler la fonction ngx_pfree pour libérer de l'espace. Cette fonction recherchera de la mémoire dans la liste chaînée des grands espaces et libérera la mémoire lorsqu'elle sera trouvée. . Pour les petites mémoires, aucun traitement n'est effectué.
Pour les gros blocs de mémoire, nginx stockera la mémoire dans une liste chaînée et la gérera via pool->large. Il convient de noter que la structure ngx_pool_large_t pour la grande mémoire gérée par l'utilisateur est appliquée à partir d'un petit bloc de mémoire dans ce pool de mémoire, ce qui signifie que ces mémoires ne peuvent pas être libérées directement par nginx. Lorsque l'utilisateur a besoin de demander un grand espace mémoire, utilisez la bibliothèque de fonctions c malloc pour demander l'espace, puis montez-le sur une certaine structure ngx_pool_large_t. Lorsque nginx a besoin d'une nouvelle structure ngx_pool_large_t, il vérifiera d'abord les trois premiers éléments de la liste chaînée pool->large pour voir s'il y en a une disponible. Si tel est le cas, elle sera utilisée directement. Sinon, elle créera un nouveau ngx_pool_large_t. structure.
3. Gestion de la mémoire partagée nginx
3.1 Description
La version de nginx utilisée dans ce part Il s'agit de la version 1.15.3
Pour plus de détails sur le code source de cette partie, veuillez consulter src/core/ngx_slab.c, src/core/ngx_shmtx.c
Partage nginx Le contenu de la mémoire est relativement volumineux, cet article ne donne donc qu'un bref aperçu.
3.2 Utilisation directe de la mémoire partagée
3.2.1 Notions de base
Besoin de créer un interface interactive dans nginx Le verrou d'exclusion est utilisé pour la synchronisation ultérieure de plusieurs processus. De plus, nginx peut avoir besoin de certaines informations statistiques, telles que les paramètres (stat_stub). Nous n'avons pas besoin de gérer spécialement ces variables, il suffit d'ouvrir l'espace partagé et de les utiliser directement.
Les informations statistiques requises après la définition de stat_stub sont également placées dans la mémoire partagée. Nous utilisons uniquement le verrou mutex dans nginx pour explication ici.
3.2.2 Implémentation du verrouillage mutex nginx
verrouillage mutex nginx, il existe deux solutions Lorsque le système prend en charge les opérations atomiques, les opérations atomiques. sont utilisés et les verrous de fichiers sont utilisés lorsqu'ils ne sont pas pris en charge. Voir la fonction ngx_event_module_init pour le code source de cette section.
La figure suivante est un diagramme schématique du verrouillage de fichiers implémentant le verrouillage d'exclusion mutuelle.
La figure suivante est un diagramme schématique de l'opération atomique pour mettre en œuvre un verrouillage mutex.
Problème
Lors du rechargement, le maître nouvellement démarré demande l'ancien one Le maître quitte directement après l'envoi du signal. L'ancien maître recharge la configuration (fonction ngx_init_cycle) et crée un nouveau processus de travail. Le nouveau processus de travail utilise le même verrou que l'ancien processus de travail.
Lors d'une mise à niveau en douceur, l'ancien maître créera un nouveau maître, et le nouveau maître héritera du port d'écoute de l'ancien maître (le fd correspondant à la socket d'écoute est passé via la variable d'environnement), et le le nouveau processus ne sera pas redémarré. Liez le port d'écoute. Il peut y avoir des situations où de nouveaux et d'anciens travailleurs écoutent un certain port en même temps. Dans ce cas, le système d'exploitation garantira qu'un seul processus gère l'événement (bien que epoll_wait soit réveillé).
3.3 Gérer la mémoire partagée via slab
nginx permet à chaque module d'ouvrir un espace partagé pour une utilisation, comme le module ngx_http_limit_conn_module.
Les idées de base de la gestion de la mémoire partagée nginx sont :
1. Allouez la mémoire en fonction des pages, et la taille de chaque page est la suivante. pareil, ici défini sur page_size.
2. Divisez le bloc mémoire selon la puissance entière de 2. Le minimum est de 8 bits et le maximum est page_size/2. Par exemple, en supposant que la taille de chaque page est de 4 Ko, la mémoire est divisée en 9 types : 8, 16, 32, 64, 128, 256, 512, 1024, 2048, chacun correspondant à cet instant, à un emplacement. la taille n du tableau de slots est de 9. Lorsque vous demandez un petit bloc de mémoire (demandez une taille de mémoire
3. Chaque page ne sera divisée qu'en un seul type de bloc mémoire. Par exemple, lors d'une demande de mémoire, la mémoire existante ne peut pas répondre aux exigences. À ce stade, une nouvelle page sera utilisée, et cette nouvelle page n'allouera qu'une mémoire de cette taille à l'avenir.
4. Connectez toutes les pages gratuites via une liste doublement chaînée. La variable free dans ngx_slab_pool_t dans la figure est utilisée pour lier des pages gratuites.
5. Liez les pages utilisées par tous les petits blocs de mémoire via le tableau des slots.
6. Pour les demandes d'espace supérieures ou égales à la taille de la page, calculez le nombre de pages requises, recherchez les pages gratuites en continu, renvoyez l'adresse de la page d'accueil de la page gratuite au client et identifiez-la via le structure de gestion ngx_slab_page_t de chaque page.
7. Toutes les pages n'auront que 3 statuts : libre, non complet et complet. Les pages gratuites et non complètes sont intégrées via une liste chaînée bidirectionnelle. Les pages complètes n'existent avec aucune page. Lorsque l'espace est libéré, il sera ajouté à une liste chaînée.
Le schéma de structure de base de la mémoire partagée nginx est le suivant :
Dans l'image ci-dessus, à l'exception d'une section de mémoire partant de l'interface ngx_slab_pool_t à l'extrême droite, qui se situe dans la zone de mémoire partagée, les autres mémoires ne sont pas de la mémoire partagée.
La mémoire partagée est finalement allouée à partir de la page.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!