Aller au contenu


Les segments mémoire partagé et sémaphore en PHP


1 réponse à ce sujet
  • Vous ne pouvez pas répondre à ce sujet

#1
goldbergg

  • messages 176
  • Inscrit(e) : 06 octobre 2011

  • Humeur du actuel : Aucun choisi

Réputation : 51 (Se fait un nom)
Les segments mémoire partagé et sémaphore enPHP

Introduction:
Un segment de mémoire partagé est un espace réservé en mémoire vive qui sera partagé avec différent programme ce qui permet donc de faire circuler des informations d'un programme a l'autre sans avoir a passé par des fichiers ou une base de donnée. En PHP les segments de mémoire partagé vont permettre de pouvoir stocker des informations qui pourront être commune à tous les utilisateurs connectés, la mémoire partagé peut aussi servir de mémoire cache, qui a l’inverse de fichier de cache aura un niveau d’accès très rapide (aucune ecriture sur disque).

Sommaire :

· Fonctions à connaitre

· Prérequis

· Exemple simple d’utilisation

· Utilisation avancé

· Sémaphore pour Linux ET Windows


1. Fonctions à connaitre
print_r() : print_r() est une fonction qui peut prendre n’importe qu’elle type de variable (string,int, objet, tableaux , etc…) et qui en affichera tous le contenu de façon lisible. Cette fonction servira a testé le contenu de la mémoire partagé.

Balise Html<pre></pre> : ces balise serve à afficher du texte pré-formaté sans avoir a passé par des balise de mise en forme ou le CSS. Ces balise serviront a affiché le retour de la fonction print_r() de façon parfaitement lisible.

strlen : retourne la taille d’une chaine de caractère.

Explode : découpe une chaine de caractères en segment à partir d’un caractère séparateur mi en paramètre, retourne un tableau.

file_exists :permet de tester si un fichier existe, retourne un booléen.

fopen :Ouvre un fichier ou le créé si il n’existe pas.

fclose :ferme un fichier.

unlick :supprime un fichier.


2. Prérequis
· Connaitre les bases du PHP.

· Avoir activé l’extension shmop de php.


3. Exemple simple d’utilisation.
La mémoire partagé en PHP s’utilise un peu comme un fichier, il s’ouvrira avec un mode (a : accès, c : création, w : lecture écriture, n : nouveau segment) et aura un niveau de permission qu’il faudra passer sous forme octale (ex : 777 pour avoir tous les droit).

Un segment sera repérable grâce à un identifiant système en Décimale ou en Hexa (ex : 0xff3).

Un segment ne peut contenir que du texte, il est donc impossible de vouloir y stocké des objet ou des tableaux (on verra toutefois un moyen de contourner le problème plus tard)

Enfin à la création d’un segment il faudra spécifier la taille que l’on veut réserver en octet.

Un segment ne peut être utilisé que par une seul ressource à la fois d’où la nécessité d’utilisé des sémaphores pour contrôler l’accès.

Voici l’exemple d’utilisation de php.net :

<?php // Crée 100 octets de mémoire partagée avec un identifiant système "0xff3"
$shm_id = shmop_open(0xff3, "c", 0644, 100);
if(!
$shm_id) {
echo
"Impossible de créer la mémoire partagée\n";
}

// Lire la taille de la mémoire partagée
$shm_size = shmop_size($shm_id);
echo
"Un bloc de SHM de taille " . $shm_size . " a été créé.\n";
// Ecriture d'une chaîne de test dans ce segment
$shm_bytes_written = shmop_write($shm_id, "Mon bloc de mémoire partagée", 0);
if (
$shm_bytes_written != strlen("Mon bloc de mémoire partagée")) {
echo
"Impossible d'écrire toutes les données en mémoire\n";
}

// Lecture du segment
$my_string = shmop_read($shm_id, 0, $shm_size);
if(!
$my_string) {
echo
"Impossible de lire toutes les données en mémoire\n";
}
echo
"Les données mises en mémoire partagées sont : " . $my_string . "\n";
// Maintenant, effaçons le bloc, et fermons le segment de mémoire
if(!shmop_delete($shm_id)) {
echo
"Impossible d'effacer le segment de mémoire";
}
shmop_close($shm_id); ?>

Cette exemple que je vous invite à tester est relativement simple, il contient beaucoup de test de vérification, voici la partie qui nousintéresse :

<?
php $shm_id = shmop_open(0xff3, "c", 0644, 100);
$shm_size = shmop_size($shm_id);
$shm_bytes_written = shmop_write($shm_id, "Mon bloc de mémoire partagée", 0);
$my_string = shmop_read($shm_id, 0, $shm_size);
shmop_close($shm_id);
?>

Dans l’ordre,

-On créé le segment, ce qui nous retourne un ID de mémoirepartagé $shm_id.

-On récupère la taille de notre segment grâce a sont ID.

-On écrie une chaine dans notre segment, la variable de retour contient la chaine.

-On lit ce qui est contenu dans le segment. Le deuxième paramètre défini l’octet de départ à lire et le troisième le nombre d’octet à lire.

-On détruit le segment.


4. Utilisation avancé
On vient de voir comment créé un segment simple, le lire puis le détruire.

Toutefois ça n’est pas vraiment efficasse si l’on a une multitude donnée a stocké en mémoire partagé, de plus viens un autre problème en testant le code suivant :

<?
php $shm_id = shmop_open(0xff3, "c", 0644, 100);
$shm_size = shmop_size($shm_id);
$shm_bytes_written = shmop_write($shm_id, "Voila ma grande chaine decaratére que voila", 0);
$shm_bytes_written = shmop_write($shm_id, "et une petite chaine", 0);
$my_string = shmop_read($shm_id, 0, $shm_size);
echo $my_string;
shmop_close($shm_id);
?>

Le texte suivants’affiche à l’écran:

et unepetite chainene de caratére que voila

Le deuxième appel de la fonction shmop_write() n’a pas écrasé l’ancienne valeur mais a réécrit par-dessus laissant ainsi la fin de la première chaine concaténé a la deuxième.

La Solution consistera à concaténer un caractère défini à la fin de la chaine que l’on inscrira dans le segment et a utilisé la fonction explode() pour récupérer uniquement se qui nous intéresse.



<?php
$shm_id =
shmop_open(0xff3, "c", 0644, 100);
$shm_size =
shmop_size($shm_id);

$Ma_Chaine =
"Voila ma grande chaine de caratére que voila";
$Ma_Chaine.=
"§"; //On concaténe le caratére de fin
$shm_bytes_written = shmop_write($shm_id, $Ma_Chaine, 0);
//La chaine en mémoire est "Voila ma grande chaine de caratére que voila§"

$Ma_Chaine = "et une petite chaine";
$Ma_Chaine.="§"; //On concaténe le caratére de fin
$shm_bytes_written = shmop_write($shm_id, $Ma_Chaine, 0);

et une petite chaine§e de caratére que voila§"
$my_string = shmop_read($shm_id, 0, $shm_size);
$TAB = explode("§",$my_string); //On segmente notre chaine a partir du caratére défini
echo"<pre>";
print_r($TAB);// on test de contenu du tableau retourner par la fonction explode
echo"</pre>";
echo $TAB[0]; //on a bien notre valeur attendu
shmop_close($shm_id);
?>

Se qui nous affiche a l’ecran :

Array

(

   [0] => et une petite chaine

   [1] => e de caratére que voila

   [2] => 

)

et une petite chaine
On retrouve bien notre chaine de caractère.
(J'ai utilisé le caractére § mais j'aurai pu utilisé n'importe lequel, toutefois il faut être sur que le caractére utilisé soit un caractére que l'utilisateur ne sésira pas ou faire en sorte qu'il ne puisse le saisire)

Maintenant que l’on a cette technique, imaginons une structure de donnée on en concaténant certains caractères au bon endroit l’on pourrait facilement créé des tableaux, voire même des tableaux de tableaux.

Voici un exemple se structure de donnée inventé pour l'exemple:

Tableau a 1 dimention:
Valeur 1¤Valeur 2¤Valeur 3¤Valeur 4¢
on sépare les case avec ¤ et on fini le tableau avec ¢

maintenant un tableau de tableaux:

Tab 1 Valeur 1¤Tab 1 Valeur 2¤Tab 1 valeur 3¤Tab 1 Valeur 4§Tab 2 Valeur 1¤Tab 2 Valeur 2¤Tab 2 valeur 3¤Tab 2 Valeur 4§Tab 3 Valeur 1¤Tab 3 Valeur 2¤Tab 3 valeur 3¤Tab 3 Valeur 4¢
§ sert a separé les tableaux
Exemple plus complet en trois fichier, init.php qui initialise la mémoire partagé, utilisation.php qui utilise le segment etfin.php qui detruit le segment.
 /** 
On va créer un tableaux de tableaux personalisé (tableau a 2 dimention)
¤ délimite une case
§ délimite un tableaux
¢ délimite la fin du tableaux de tableaux

**/
//on génére la chaine de caractères
$TS = time();
$ID = "12346789";
$Message = "";
$Salon = "";
for($i=0;$i<256;$i++) $Message.="x";
for($i=0;$i<64;$i++) $Salon.="x";
$enregistrement = $TS."¤".$ID."¤".$Message."¤".$Salon;
$mem="";
// on initilise la variable mémoire avec 500 enregistrements
for($i=0;$i<500;$i++) {
$mem.=$enregistrement;
if($i!=499) $mem.="§";
}
$mem.="¢";
// création du block mémoire partagé
$shm_id = @shmop_open(0xf00, "c", 0644, strlen($mem));
if(!$shm_id) { // on vérifie si la créatioon du block mémoire partagé a réussi
echo "<div>Erreur: Impossible de créer la mémoire partagée.</div>";
} else {
$shm_bytes_written = @shmop_write($shm_id, $mem, 0);
echo strlen($mem);
}
?>


    $taille = 171000; 
$shm_id = @shmop_open(0xf00, "w", 0, 0);
if ($shm_id) {
$my_string = shmop_read($shm_id, 0, $taille);
$shm_size = shmop_size($shm_id);
echo $shm_size;
$explode = explode("¢",$my_string);
$explode = explode("§",$explode[0]);
$i = 0;
$TAB = array();
foreach ($explode as &$ligne){
$TAB[$i] = explode("¤",$ligne);
$i++;
}
for($i=0;$i<500;$i++){
$TAB[$i][0] = time();
$TAB[$i][1] = $i;
$TAB[$i][2] = "Voici mon message";
$TAB[$i][3] = "Salon Principal";
}
$mem = "";
for($i=0;$i<500;$i++) $mem.=$TAB[$i][0]."¤".$TAB[$i][1]."¤".$TAB[$i][2]."¤".$TAB[$i][3]."§";
$mem.="¢";
// On modifie la mémoire partagé
$shm_bytes_written = @shmop_write($shm_id, $mem, 0);
$shm_size = shmop_size($shm_id);
echo $shm_size;
//on la recharge
$my_string = shmop_read($shm_id, 0, $taille);
$shm_size = shmop_size($shm_id);
echo $shm_size;
$explode = explode("¢",$my_string);
$explode = explode("§",$explode[0]);
echo"";
$i = 0;
$TAB = array();
foreach ($explode as &$ligne){
$TAB[$i] = explode("¤",$ligne);
$i++;
}
//on test si la modification a bien eu lieux
print_r($TAB);
echo"";
}
}else{
echo "erreur";
}
?>


      $shm_id = @shmop_open(0xf00, "w", 0, 0);  
shmop_delete($shm_id);
?>


Voilà vous êtes en mesure de stocker des tableaux a N dimensions en mémoire partagé.

J’ai créé ma propre structure pour l’exemple, mais rien n’empêche d’utiliser des structures déjà existante tel que le JSOn ou le XML.


4. Sémaphore pour Linux ET Windows
Il reste maintenant un dernier détail a réglé, l’accès au segment. Comme je l’ai dit précédemment, seule une ressource à la fois à accès au segment, c’est là qu’interviendront les sémaphores.

Les sémaphores permettent de protégé l’accès à une ressource et fonctionne avec un système d’autorisation, voici le fonctionnement :

Soit trois ressource A, B et C , A étant la mémoire partagé

- Les sémaphores protègent l’accès de A.

- B veux accéder a A, il demande donc un accès

-Aucune ressource n’utilise A, l’accès est donc autorisé.

-B a accès A.

- C veux accéder a A, il demande donc un accès

-B utilise déjà A, l’accès est refusé

-B n’a plus besoin de A, il libère donc l’accès

-C se voit attribué l’accès a A.

-etc…

En PHP on utilisera la fonction sem_get avec en paramètre l’ID system du segment pour demander l’accès, sem_acquire permettra de vérifier si l’accès est autorisé et si tel est le cas de verrouillé l’accès, enfin sem_release permettra de libérer l’accès.

// On demande l’accès
$ok = sem_get(0xf00);
// l'accès au block mémoire est autorisé
if(sem_acquire ($ok)) {
/*
le code

*/

// on libère l'accès
sem_release($ok); }




Petit problème toutefois sous Windows, les fonctions de sémaphorene sont pas compatibles.

Problème que l’on ressoudera avec un fichier :

if(!file_exists('sem.txt')) { 		
$SEM = fopen('sem.txt', 'w');
/*
Le code

*/
fclose($SEM);
unlink('sem.txt');
}


On test si le fichier sem.txt existe, si il n’existe pas on a l’accès on le créé et on le laisse ouvert, pour libérer l’accès, on ferme le fichier et on le supprime.


Conclusion
Voilà si vous avez bien compris vous devriez être en mesure de pouvoir utilisé la mémoire partagé assez simplement et pour toute sorte d’utilisation.

Voici un fichier de ressource :

MemoirePartage.zip

Il contient deux exemple pour Linux et deux exemple pour Windows, ces exemple sont les base de la création d’un Chat en AJAX sans base de donnée et sans fichier.

Si vous avez des questions ou des remarques je reste a votredisposition.

Modifié par goldbergg, 10 décembre 2011 - 13:27.

  • 2

#2
Raitoryuuku

  • messages 185
  • Inscrit(e) : 28 juillet 2011

  • Humeur du actuel : Aucun choisi

Réputation : 7 (Neutre)
Très chouette tutoriel !

je n'ai pas encore eu besoin de partager des informations pour tout les visiteurs, néanmoins sa peut-être un gain de ressource si on a le courage de codé un petit système pour éviter les sur-appel a la BDD.
  • 0
Hotensai.fr Recrute un(e) rédacteur(trice) (pour la section Anime/manga principalement) ! si vous avez des questions ou si vous êtes intéressé MP moi.





0 utilisateur(s) dans ce forum

0 membre(s), 0 invité(s), 0 utilisateur(s) anonyme(s)

Community Forum Software by IP.Board
Licence accordée à : AnimeServ.NET
Design & Code by Bad.Y o/
© 2013 AnimeServ