Différences entre versions de « Php daemon »
Ligne 122 : | Ligne 122 : | ||
Voici une classe ''Server'' qui implémente la classe ''Daemon'' : | Voici une classe ''Server'' qui implémente la classe ''Daemon'' : | ||
<div style="width:700px"> | <div style="width:700px"> | ||
− | <source lang="php" style=" | + | <source lang="php" style="font-size:110%"> |
<?php | <?php | ||
class Server extends Daemon { | class Server extends Daemon { | ||
Ligne 154 : | Ligne 154 : | ||
Et enfin, un fichier qui nous permet d'instancier et de démarrer la classe ''Server'' : | Et enfin, un fichier qui nous permet d'instancier et de démarrer la classe ''Server'' : | ||
<div style="width:700px"> | <div style="width:700px"> | ||
− | <source lang="php" style=" | + | <source lang="php" style="font-size:110%"> |
<?php | <?php | ||
include_once 'class/Daemon.class.php'; | include_once 'class/Daemon.class.php'; |
Version du 9 mars 2017 à 07:16
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;
private $signals = array (
SIGTERM,
SIGINT,
SIGCHLD,
SIGHUP
);
/**
* Class used to handle POSIX signals and fork from the current process
*
* @param string $name
* <p>The name of the class</p>
* @param array $signals
* <p>An array containing additional POSIX signals to handle [optionel] </p>
*/
protected function __construct($name, array $signals = array()) {
$this->name = $name;
if (! empty ( $signals )) {
$this->signals = array_merge ( $this->signals, $signals );
}
// Permet au script PHP de s'éxécuter indéfiniment
set_time_limit ( 0 );
$this->registerSignals ();
}
/**
* Used to register POSIX signals
*/
private function registerSignals() {
declare ( ticks = 1 )
;
foreach ( $this->signals as $signal ) {
@pcntl_signal ( $signal, array (
'Daemon',
'handleSignal'
) );
}
}
/**
* Used to handle properly SIGINT, SIGTERM, SIGCHLD and SIGHUP
*
* @param string $signal
*/
protected function handleSignal($signal) {
if ($signal == SIGTERM || $signal == SIGINT) {
// Gestion de l'extinction
$this->isRunning = false;
} else if ($signal == SIGHUP) {
// Gestion du redémarrage
$this->onStop ();
$this->onStart ();
} else if ($signal == SIGCHLD) {
// Gestion des processus fils
pcntl_waitpid ( - 1, $status, WNOHANG );
} else {
// Gestion des autres signaux
$this->handleOtherSignals ( $signal );
}
}
/**
* Launch the infinite loop executing the ''run'' abstract method
*/
protected function start() {
$this->onStart ();
while ( $this->isRunning ) {
$this->run ();
}
$this->onStop ();
}
/**
* Override to implement the code run infinetly
*/
protected abstract function run();
/**
* Override to execute code before the ''run'' method on daemon start
*/
protected abstract function onStart();
/**
* Override to execute code after the ''run'' method on daemon shutdown
*/
protected abstract function onStop();
/**
* Override to handle additional POSIX signals
*
* @param int $signal
* <p>Signal sent by interrupt</p>
*/
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() {
// Ici on souhaite gérer les signaux SIGUSR1 et SIGUSR2 en plus
parent::__construct ( "Server", array (
SIGUSR1,
SIGUSR2
) );
// Démarrage du démon
parent::start ();
}
public function run() {
// Le code qui s'exécute infiniment
echo "On tourne !\n";
sleep ( 5 );
}
public function onStart() {
echo "Démarrage du processus avec le pid " . getmypid () . "\n";
}
public function onStop() {
echo "Arrêt du processus avec le pid " . getmypid () . "\n";
}
public function handleOtherSignals($signal) {
echo "Signal non géré par la classe Daemon : " . $signal . "\n";
}
}
?>
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
Détachement du processus courant
Lorsque l'on exécute le script start.php, celui-ci ne rend pas la mains, ce qui pose problème si on veut un vrai démon. Nous allons donc utiliser la fonction pcntl_fork.
La modification se fait dans la fonction protected function start() :
protected function start() {
$pid = pcntl_fork ();
if ($pid == false) {
} else if ($pid) {
// Processus courant (père)
} else {
// Processus fils (démon)
$this->onStart ();
while ( $this->isRunning ) {
$this->run ();
}
$this->onStop ();
}
}
Si la fonction renvoie false, les forks ne sont pas supportés (eg. sur Windows), sinon, dans le processus père la variable $pid correspond au numéro de PID du processus fils et dans le fils elle aura la valeur 0.