gestion de la mémoire

La gestion de la mémoire du moteur est implémentée avec des fonctionnalités importantes pour un système comme PHP. La fonctionnalité exacte de la gestion de la mémoire du moteur et les optimisations effectuées sortent du cadre de ce document. Cependant, une bonne compréhension de ses fonctionnalités fournit une base pour une bonne compréhension, et vous présente la terminologie et les fonctionnalités utilisées dans PHP.

La plus importante de ses caractéristiques, et la première chose à mentionner est le suivi des allocations. Les allocations de suivi permettent au gestionnaire de mémoire d'éviter les fuites. Quand PHP est construit en mode débogage (--enable-debug), les fuites détectées sont signalées, dans un monde parfait, elles ne seraient jamais déployées. Essayez toujours de résoudre les fuites avant de déployer votre code, une fuite de mémoire dans un environnement SAPI peut devenir un très gros problème, très rapidement.

Une autre caractéristique, peut-être plus accessoire mais encore remarquable, est que le gestionnaire de mémoire est la partie qui permet une limite stricte de l'utilisation de la mémoire pour chaque instance de PHP. Comme nous le savons tous, il n'y a pas d'illimité. Si un code manque de mémoire, il est susceptible d'être mal écrit par le programmeur de PHP. Limiter la mémoire n'est donc pas une restriction sur le langage qui est supposé être expérimenté en production, c'est simplement un moyen d'empêcher les environnements de développement de devenir incontrôlables lorsque des erreurs sont commises, et également lorsque des bogues sont trouvés en production.

API de mémoire principale

PrototypeDescription
void *emalloc(size_t size)Alloue la taille octets de la mémoire.
void *ecalloc(size_t nmemb, size_t size)Alloue un tampon pour les éléments nmemb de taille octets et s'assure qu'il est initialisé avec des zéros.
void *erealloc(void *ptr, size_t size)Redimensionner le tampon ptr, qui a été alloué en utilisant emalloc pour contenir des octets de taille de la mémoire.
void efree(void *ptr)Libère le tampon pointé par ptr. Le tampon devait être alloué par emalloc.
void *safe_emalloc(size_t nmemb, size_t size, size_t offset)Alloue une mémoire tampon pour contenir des blocs nmemb de chaque taille octets et un octet de décalage supplémentaire. Ceci est similaire à emalloc (nmemb * size + offset) mais ajoute une protection spéciale contre les débordements.
char *estrdup(const char *s)Alloue un tampon qui peut contenir la chaîne NULL-terminated et copier le s dans ce tampon.
char *estrndup(const char *s, unsigned int length)Semblable à estrdup alors que la longueur de la chaîne NULL-terminated est déjà connue.

Lors de l'exécution dans un environnement de débogage, configuré avec --enable-debug et --enable-zend-test, la fonction de fuite utilisée dans l'exemple suivant est réellement implémentée par le moteur et est disponible pour appeler dans userland.

Exemple : Détection de fuite en action

C#ZEND_FUNCTION(zend_leak_bytes)
{
    zend_long leakbytes = 3;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &leakbytes) == FAILURE) {
        return;
    }

    emalloc(leakbytes);
}

L'exemple ci-dessus va afficher quelque chose de similaire à :

C#$ sapi/cli/php -r 'zend_leak_bytes();'
[Mon Apr 23 17:43:42 2018]  Script:  'Standard input code'
/home/mbaynton/projects/php-src/ext/zend_test/test.c(106) :  Freeing 0x00007f3203c92050 (3 bytes), script=Standard input code
=== Total 1 memory leaks detected ===

Persistance des données

Dans ce contexte, la persistance des données signifie toute donnée destinée à survivre à la demande en cours. La gestion de la mémoire dans le moteur est très concentrée sur les allocations liées aux demandes, mais ce n'est pas toujours pratique ou approprié. La mémoire persistante est parfois requise afin de satisfaire aux exigences des bibliothèques externes.

Une utilisation courante de la mémoire persistante est de permettre des connexions SQL Server persistantes, bien que cette pratique soit désapprouvée, elle n'en reste pas moins l'utilisation la plus courante de cette fonctionnalité.

API de mémoire persistante

PrototypeDescription
void *pemalloc(size_t size, zend_bool persistent)Alloue la taille octets de la mémoire.
void *pecalloc(size_t nmemb, size_t size, zend_bool persistent)Alloue un tampon pour les éléments nmemb de taille octets et s'assure qu'il est initialisé avec des zéros.
void *perealloc(void *ptr, size_t size, zend_bool persistent)Redimensionne le tampon ptr, qui a été alloué en utilisant emalloc pour contenir des octets de taille de la mémoire.
void pefree(void *ptr, zend_bool persistent)Libère le tampon pointé par ptr. Le tampon devait être alloué par pemalloc.
void *safe_pemalloc(size_t nmemb, size_t size, size_t offset, zend_bool persistent)Alloue une mémoire tampon pour contenir des blocs nmemb de chaque taille octets et un octet de décalage supplémentaire. Ceci est similaire à pemalloc (taille nmemb * + offset) mais ajoute une protection spéciale contre les débordements.
char *pestrdup(const char *s, zend_bool persistent)Alloue un tampon qui peut contenir la chaîne NULL-terminated et copier le s dans ce tampon.
char *pestrndup(const char *s, unsigned int length, zend_bool persistent)Similaire à pestrdup alors que la longueur de la chaîne NULL-terminated est déjà connue.

Gestionnaire de ressources Thread-Safe

Lorsque PHP est construit avec Thread Safety activé, le moteur nécessite un moyen d'isoler les contextes les uns des autres, de sorte que les threads d'un processus puissent traiter des demandes individuelles sans interférence. TSRM est omnipotent dans PHP, les auteurs d'extension doivent faire très peu pour s'assurer que leurs extensions peuvent fonctionner à la fois dans une architecture Thread Safe et Non Thread Safe.

Exemple : Macros d'accès pour les globales par module

C##ifdef ZTS
#define COUNTER_G(v) TSRMG(counter_globals_id, zend_counter_globals *, v)
#else
#define COUNTER_G(v) (counter_globals.v)
#endif

L'extrait ci-dessus montre comment un auteur d'extension doit définir ses accesseurs globaux. La macro TSRMG prend un identifiant, un type cast et un nom d'élément. L'identifiant est assigné par TSRM lors de l'initialisation du module. La déclaration des accesseurs globaux de cette manière garantit qu'une extension peut fonctionner en toute sécurité dans une architecture Thread Safe et Non Thread Safe en utilisant la même logique.

TSRM gère l'isolation et la sécurité de toutes les structures globales au sein de PHP, des globales d'exécution aux globales d'extension, un pointeur vers le stockage isolé est également transmis avec la plupart ou la plupart des fonctions de l'API. Les macros TSRMLS_C et TSRMLS_CC se traduisent respectivement par "stockage local sûr" et "stockage local sécurisé avec préfixe avec une virgule".

Si une fonction nécessite un pointeur sur TSRM, elle est déclarée avec la macro TSRMLS_D ou TSRMLS_DC dans son prototype, qui se traduit respectivement par "stockage local sécurisé uniquement" et "stockage local sécurisé avec préfixe avec une virgule". Beaucoup de macros dans le moteur référencent TSRM, donc c'est une bonne idée de déclarer la plupart des choses à accepter TSRM, de sorte que s'ils ont besoin d'appeler TSRM, ils n'ont pas à récupérer un pointeur pendant l'exécution.

Comme TSRM est un thread local et que certaines fonctions (pour des raisons de compatibilité) ne peuvent pas accepter directement le TSRM, la macro TSRMLS_FETCH existe, qui demande à TSRM d'extraire le pointeur vers le stockage local du thread. Cela devrait être évité partout où il peut être, car il n'est pas sans coût dans une configuration Thread Safe.

La fonctionnalité documentée ci-après est destinée à l'utilisation avancée de TSRM. Il n'est pas normal que les extensions doivent interagir directement avec TSRM, le programmeur pecl doit utiliser les API TSRM ci-dessus, telles que l'API Module Globals.

TSRM Internes

PrototypeDescription
typedef int ts_rsrc_idLa définition de type pour représenter une ressource par des identifiants numériques
int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename)expected_threads et expected_resources ne sont pas des limites strictes. debug_level et debug_filename ont uniquement un effet sur les fenêtres en mode Debug ou lorsque TSRM_DEBUG est défini. Appelé une fois par processus en mode ZTS lors du démarrage du serveur.
void tsrm_shutdown(void)Devrait être la dernière chose appelée dans le processus pour détruire et libérer tous les stockage TSRM de stockage en mode ZTS.
ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)Alloue et met en sécurité le pointeur de taille octets, appelant ctor sur le pointeur, assignant (et retournant) l'id à rsrc_id, le dteur est appelé quand la ressource est libre. Les globals de module sont isolés en mode ZTS à l'aide de cette API.
void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)Récupère une ressource par l'ID précédemment attribué à partir des entrées créées par le thread spécifié identifié par th_id.
void *ts_resource(ts_rsrc_id id)Récupère une ressource par l'ID précédemment attribué à partir des entrées créées par le thread appelant.
void ts_free_thread(void)Détruit et libère les entrées pour le thread appelant, cette API est actuellement utilisée uniquement par le module ISAPI.
void ts_free_id(ts_rsrc_id id)Détruit la ressource identifiée par l'ID précédemment attribué. Les globals de module sont nettoyés en mode ZTS à l'aide de cette API.

API TSRM Mutex

PrototypeDescription
MUTEX_T tsrm_mutex_alloc(void)Alloue et renvoie un MUTEX_T approprié pour l'environnement.
int tsrm_mutex_lock(MUTEX_T mutexp)Verrouille mutexp pour le thread appelant.
int tsrm_mutex_unlock(MUTEX_T mutexp)Déverrouille mutexp pour le thread appelant.
void tsrm_mutex_free(MUTEX_T mutexp);Détruit et libère le mutexp (déverrouillé) et précédemment alloué.

les réactions

Pour laisser un avis, vous devez être inscrit et connecté

Se connecter