Shared Memory in PHP


php

Navigando sul web ho trovato poca documentazione a proposito della gestione della shared memory mediante PHP, ed anche il manuale ufficiale non è molto chiaro. Ho deciso di fare un esperimento, ed ho scoperto che è un ottimo metodo per far comunicare due sessioni senza far uso intensivo di database e file temporanei, con il grande vantaggio di poter usare le potenzialità offerte dai semafori, senza dove riscrivere del proprio codice per questa delicata operazione.

Gli utilizzi sono molteplici, nel mio caso si tratta di far comunicare due applicazioni flash usando delle pagine php come ponte, nel modo più veloce e meno oneroso possibile. La prima applicazione inizializza semaforo, memoria condivisa e variabile di comunicazione, sfruttando come discriminante il proprio id di sessione.

<?php
/* start.php */
session_start();

$MEMSIZE = 512; //  dimensione della memoria condivisa (byte)

$filename = session_save_path() . '/sess_' . session_id();
echo "$filename<br />\n";

$SEMKEY = ftok('/','S');    //  key semaforo
$SHMKEY = ftok('/','M');    //  key shared memory
echo "$SEMKEY [semaforo]<br />\n";
echo "$SHMKEY [shared mem]<br />\n";

// Creo il semaforo
$sem_id = sem_get($SEMKEY, 1);
if ($sem_id !== false) {
echo "Semaforo creato [$sem_id]<br />\n";

// Acquisisco il semaforo
if (sem_acquire($sem_id)) {
  echo "Semaforo acquisito [$sem_id]<br />\n";

  // Associo la shared mem al semaforo
  $shm_id = shm_attach($SHMKEY, $MEMSIZE);
  if ($shm_id !== false) {
   echo "Memoria condivisa associata [$shm_id]<br />\n";

   // Inializzo la variabile
   if (shm_put_var($shm_id, 1, "Variable 1 ciao")) {
    echo "Variabile inizalizzata<br />\n";

    // Rilascio il semaforo
    if (sem_release($sem_id)){
     echo "Semaforo rilasciato<br />\n";
    } else {
     echo "Errore nel rilascio del semaforo<br />\n";
     sem_remove($sem_id);
     shm_remove($shm_id);
    }

   } else {
    echo "Errore nella scrittura della variabile<br />\n";
    sem_remove($sem_id);
    shm_remove($shm_id);
   }
  } else {
   echo "Errore nell'associazione della memoria condivisa<br />\n";
   sem_remove($sem_id);
  }
} else {
  echo "Errore nell'acquisizione del semaforo<br />\n";
  sem_remove($sem_id);
}
} else {
echo "Errore nella creazione del semaforo<br />\n";
}
?>

A questo punto la sessione paritaria deve soltanto conoscere l’id della sessione con cui vuole comunicare, in modo da poter inizializzare le chiavi del semaforo e della memoria condivisa. Richiamando il codice seguente, per esempio, può leggere il valore della variabile condivisa:

/* read.php */
<?php
session_start();

if (isset($_GET['file'])){

$filename = $_GET['file'];
echo "$filename<br />\n";

$MEMSIZE = 512;             //  dimensione della memoria condivisa (byte)
$SEMKEY = ftok('/','S');    //  key semaforo
$SHMKEY = ftok('/','M');    //  key shared memory
echo "$SEMKEY [semaforo]<br />\n";
echo "$SHMKEY [shared mem]<br />\n";

// Creo il semaforo
$sem_id = sem_get($SEMKEY, 1);
if ($sem_id !== false) {
  echo "Semaforo creato [$sem_id]<br />\n";

  // Acquisisco il semaforo
  if (sem_acquire($sem_id)) {
   echo "Semaforo acquisito [$sem_id]<br />\n";

   // Associo la shared mem al semaforo
   $shm_id = shm_attach($SHMKEY, $MEMSIZE);
   if ($shm_id !== false) {
    echo "Memoria condivisa associata [$shm_id]<br />\n";

    // Leggo la variabile
    $var1 = shm_get_var($shm_id, 1);
    if ($var1 !== false) {
     echo "Variabile letta [$var1]<br />\n";

     // Rilascio il semaforo
     if (sem_release($sem_id)){
      echo "Semaforo rilasciato<br />\n";
     } else {
      echo "Errore nel rilascio del semaforo<br />\n";
      sem_remove($sem_id);
      shm_remove($shm_id);
     }

    } else {
     echo "Errore nella lettura della variabile<br />\n";
     sem_remove($sem_id);
     shm_remove($shm_id);
    }
   } else {
    echo "Errore nell'associazione della memoria condivisa<br />\n";
    sem_remove($sem_id);
   }
  } else {
   echo "Errore nell'acquisizione del semaforo<br />\n";
   sem_remove($sem_id);
  }
} else {
  echo "Errore nella creazione del semaforo<br />\n";
}
} else {

?>

<form action='read.php' method='get'>
  <input type='text' name='file' /><br />
  <input type='submit' />
</form>

<?php
}
?>

 

Richiamando (da un altro browser) la pagina senza parametri viene presentata una form che richiede l’inserimento del path del file di sessione del paritario. In questo caso basta un cut/paste dall’output della pagina precedente, in un applicazione reale ci si appoggierà ad un database. Inserendolo si prende il controllo del semaforo, ed in seguito si può leggere il contenuto della variabile di sessione. In modo del tutto analogo si può modificare il contenuto della variabile:

/* write.php */
<?php
session_start();

if (isset($_GET['file']) && isset($_GET['value'])){

$filename = $_GET['file'];
echo "$filename<br />\n";
$value = $_GET['value'];
echo "$value<br />\n";

$MEMSIZE = 512;             //  dimensione della memoria condivisa (byte)
$SEMKEY = ftok('/','S');    //  key semaforo
$SHMKEY = ftok('/','M');    //  key shared memory
echo "$SEMKEY [semaforo]<br />\n";
echo "$SHMKEY [shared mem]<br />\n";

// Creo il semaforo
$sem_id = sem_get($SEMKEY, 1);
if ($sem_id !== false) {
  echo "Semaforo creato [$sem_id]<br />\n";

  // Acquisisco il semaforo
  if (sem_acquire($sem_id)) {
   echo "Semaforo acquisito [$sem_id]<br />\n";

   // Associo la shared mem al semaforo
   $shm_id = shm_attach($SHMKEY, $MEMSIZE);
   if ($shm_id !== false) {
    echo "Memoria condivisa associata [$shm_id]<br />\n";

    // Scrivo la variabile
    if (shm_put_var($shm_id, 1, $value)) {
     echo "Variabile scritta [$value]<br />\n";

     // Rilascio il semaforo
     if (sem_release($sem_id)){
      echo "Semaforo rilasciato<br />\n";
     } else {
      echo "Errore nel rilascio del semaforo<br />\n";
      sem_remove($sem_id);
      shm_remove($shm_id);
     }

    } else {
     echo "Errore nella scrittura della variabile<br />\n";
     sem_remove($sem_id);
     shm_remove($shm_id);
    }
   } else {
    echo "Errore nell'associazione della memoria condivisa<br />\n";
    sem_remove($sem_id);
   }
  } else {
   echo "Errore nell'acquisizione del semaforo<br />\n";
   sem_remove($sem_id);
  }
} else {
  echo "Errore nella creazione del semaforo<br />\n";
}
} else {

?>

<form action='write.php' method='get'>
<input type='text' name='file' /><br />
<input type='text' name='value' /><br />
<input type='submit' />
</form>

<?php
}
?>

A questo punto non resta che da implementare una pagina per distruggere l’area di memoria condivisa, in modo da non incappare in problemi di saturazione della memoria.

Lascio la creazione di tale pagina come esercizio, dato che non trovo più il relativo sorgente :P

/* delete.php */

Il codice presentato è un semplice proof of concept, e non è da intendersi valido per un utilizzo in produzione. Mancano tutta una serie di controlli sulla corretta gestione della memoria, sulla validazione del paritario e del necessario timing nel caso in cui si cerchi di accedere ad un semaforo “rosso”. Magari in un prossimo post affronterò la questione, per ora devo andarmela a studiare :)

Happy coding!