Php daemon
Introduction
Définition
Un daemon [...], parfois traduit par démon, désigne un type de programme informatique, un processus ou un ensemble de processus qui s'exécute en arrière-plan plutôt que sous le contrôle direct d'un utilisateur. Wikipedia
Principe
Pour qu'un processus soit considéré comme un démon, il faut :
- qu'il puisse répondre au signaux du système ;
- qu'il continue son exécution avec ou sans contrôle utilisateur.
Mise en place
Réception des signaux système
Nous allons créer une classe serveur qui va répondre aux interruptions envoyées par le système. Celles qui vont nous intéresser sont :
- SIGINT : signal d’interruption déclenché par ctrl+c;
- SIGTERM : signal de terminaison déclenché par la commande kill;
- SIGCHLD : signal utilisé pour gérer les processus fils (fork)
- SIGHUP : signal utilisé pour redémarrer un processus;
Nous allons créer une classe abstraite Daemon qui va nous servir de base pour répondre aux signaux :
<?php
abstract class Daemon {
protected $name;
private $isRunning = true;
/**
* Classe permettant de répondre aux signaux système
*
* @param string $name
* Le nom de la classe
*/
protected function __construct($name) {
$this->name = $name;
// Permet au script PHP de s'éxécuter indéfiniment
set_time_limit ( 0 );
$this->registerSignals ();
}
/**
* Fonction qui permet l'enregistrement des signaux
*/
private function registerSignals() {
declare ( ticks = 1 )
;
pcntl_signal ( SIGTERM, array (
'Daemon',
'handleSignal'
) );
pcntl_signal ( SIGINT, array (
'Daemon',
'handleSignal'
) );
pcntl_signal ( SIGCHLD, array (
'Daemon',
'handleSignal'
) );
}
/**
* Fonction qui s'occupe de la gestion des signaux
*
* @param string $signal
*/
protected function handleSignal($signal) {
if ($signal == SIGTERM || $signal == SIGTERM) {
// Gestion de l'extinction
$this->onStop ();
$this->isRunning = false;
} else if ($sig == SIGHUP) {
// Gestion du redémarrage
$this->onStop ();
$this->onStart ();
} else if ($sig == SIGCHLD) {
// Gestion des processus fils
pcntl_waitpid ( - 1, $status, WNOHANG );
} else {
// Gestion des autres signaux
$this->handleOtherSignals ( $signal );
}
}
/**
* Fonction qui va permettre d'exécuter
* indéfiniment la fonction ''run''
*/
protected function start() {
$this->onStart ();
while ( $this->isRunning ) {
$this->run ();
}
}
/**
* Fonction exécutée indéfiniment
*/
protected abstract function run();
/**
* Fonction exécutée une fois au démarrage du démon
*/
protected abstract function onStart();
/**
* Fonction exécutée une fois à l'arrêt du démon
*/
protected abstract function onStop();
/**
* Fonction laissée pour la gestion des autres signaux
*
* @param string $signal
*/
protected abstract function handleOtherSignals($signal);
}
?>
La classe Daemon ne peut pas être instanciée car elle est abstraite ce qui permet de définir les méthodes abstraites onStart, onStop, run et handleOtherSignals. Le code de ces méthodes est spécifique à chaque démon et c'est pour cela que l'utilisateur qui va étendre la classe Daemon devra les surcharger.
Cela nous permet dans la classe Daemon de seulement coder le comportement du démon et non ce qu'il fait.
Utilisation
Voici une classe Server qui implémente la classe Daemon :
<?php
class Server extends Daemon {
public function __construct(){
parent::__construct("Server");
// Démarrage du démon
parent::start();
}
/**
* Fonction exécutée indéfiniment
*/
public function run(){
// Le code qui s'exécute infiniment
echo "On tourne !\n";
sleep(5);
}
/**
* Fonction exécutée une fois au démarrage du démon
*/
public function onStart(){
echo "Démarrage du processus avec le pid ".getmypid()."\n";
}
/**
* Fonction exécutée une fois à l'arrêt du démon
*/
public function onStop(){
echo "Arrêt du processus avec le pid ".getmypid()."\n";
}
/**
* Fonction laissée pour la gestion des autres signaux
*/
public function handleOtherSignals($signal){
echo "Signal non géré par la classe Daemon : ".$signal;
}
}
Et enfin, un fichier qui nous permet d'instancier et de démarrer la classe Server :
<?php
include_once 'class/Daemon.class.php';
include_once 'class/Server.class.php';
new Server();
Lorsque vous démarrer le script dans Eclipse ou dans un terminal vous devriez voir cela :
Démarrage du processus avec le pid 82108 On tourne ! On tourne ! On tourne !
Et lorsque on exécute la commande suivante dans un autre terminal :
# kill 82108
Le message suivant apparaît :
Arrêt du processus avec le pid 82108