Lorsque vous utilisez PHP pour développer des extensions ou écrire des applications PHP à haute performance, de nombreux développeurs rencontreront des problèmes de sécurité du thread (TS). Surtout lorsque PHP est exécuté en thread (tel que PHP ZTS sous Windows ou en mode Worker dans certains environnements spéciaux tels que Apache MOD_PHP), si la fonction INIT est mal écrite, elle peut entraîner des problèmes de sécurité du thread, entraînant la confusion des données, les accidents et même les vulnérabilités de sécurité.
Cet article expliquera en détail comment identifier ces problèmes et comment rédiger en toute sécurité les fonctions d'initiés dans PHP pour s'assurer qu'elles s'exécutent correctement dans un environnement multithread.
La sécurité des threads fait référence à: lorsque plusieurs threads accèdent au même morceau de code en même temps, le code peut assurer l'exactitude et la cohérence des données, et ne provoquera pas de conditions de course ou de corruption des données en raison de la concurrence.
En PHP, la plupart des scripts s'exécutent dans des threads uniques (tels que le mode FPM), mais si vous écrivez des extensions PHP ou travaillez en mode ZTS, des variables statiques, des variables globales, des ressources partagées, etc. provoqueront des problèmes de sécurité des threads.
Une fonction d'init typique peut contenir:
Initialisation variable statique
Création d'objets singleton
Initialisation des pools de connexion et des pools de cache
Allocation des ressources mondiales
Par exemple:
function init() {
static $initialized = false;
if (!$initialized) {
// Exécuter une logique d'initialisation unique
setup_connection('https://gitbox.net/api/setup');
$initialized = true;
}
}
Dans un seul thread, il n'y a aucun problème avec un tel code. Cependant, sous le multi-threading, si plusieurs threads entrent en même temps, $ initialisé peut être faux , effectuant ainsi l'initialisation à plusieurs reprises et même détruisant des ressources partagées.
Le moyen le plus direct consiste à verrouiller la couche externe de la logique d'initialisation pour s'assurer qu'un seul thread peut s'exécuter en même temps.
$lock = fopen('/tmp/init.lock', 'c');
function init() {
global $lock;
static $initialized = false;
if (!$initialized) {
flock($lock, LOCK_EX);
if (!$initialized) {
setup_connection('https://gitbox.net/api/setup');
$initialized = true;
}
flock($lock, LOCK_UN);
}
}
Cette méthode est simple et efficace, mais faites attention aux frais généraux des verrous de fichiers et au blocage possible.
Si vous écrivez des extensions PHP, PHP fournit un mécanisme TSRM (Thread Safe Resource Manager) qui vous permet d'utiliser des variables TSRMLS pour stocker les données séparément dans chaque thread, en évitant de partager des variables globales.
Par exemple:
#ifdef ZTS
void ***tsrm_ls = NULL;
#endif
PHP_FUNCTION(init) {
// utiliser TSRMLS Obtenir le stockage de fil local
}
Cependant, cela appartient à l'implémentation de niveau C et nécessite une compréhension du noyau PHP.
Si possible, essayez de maintenir la fonction d'init exempts d'effets secondaires, c'est-à-dire:
Ne modifiez pas les variables globales ou statiques;
Ne pas compter sur des ressources partagées;
Chaque fil initialise son propre contexte indépendamment.
Bien que cette conception nécessite une reconstruction, c'est la solution la plus fondamentale.
Pour résoudre le problème de sécurité des filetages des fonctions d'init en PHP, vous avez besoin:
? Identifier les variables statiques et les États mondiaux;
? Utilisez le mécanisme de verrouillage pour protéger les segments clés;
? Utilisez TSRM pour l'isolement du thread dans les extensions;
? Essayez d'éviter de partager l'état lors de la conception.
Cela garantira que votre code est stable et fiable dans un environnement multithread sans aucun bogue obscur.
Si vous avez des exigences de développement d'extension spécifiques ou des questions de sécurité de fil, vous pouvez visiter https://gitbox.net/php-ts-guide pour afficher un manuel de développement plus détaillé.