Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> Demon UNIXowy w php, Czyli słow kilka o egzorcyzmach ;D
Seth
post
Post #1





Grupa: Przyjaciele php.pl
Postów: 2 335
Pomógł: 6
Dołączył: 7.03.2002

Ostrzeżenie: (0%)
-----


Pod wplywem tematu 1);
define('DLOG_NOTICE'2);
define('DLOG_WARNING'4);
define('DLOG_ERROR'8);
define('DLOG_CRITICAL'16);

/**
 * Daemon base class
 *
 * Requirements:
 * Unix like operating system
 * php 4 >= 4.3.0 or php 5
 * php compiled with:
 * --enable-sigchild
 * --enable-pcntl
 *
 * @package binarychoice.system.unix
 * @author Michal 'Seth' Golebiowski <seth at binarychoice dot pl>
 * @copyright Copyright 2005 Seth
 * @since 1.0.3
 */
class Daemon
{
 
/**#@+
* @access public
*/
 /**
* User ID

* @var int
* @since 1.0
*/
 
var $userID 99;

 
/**
* Group ID

* @var integer
* @since 1.0
*/
 
var $groupID 99;
 
 
/**
* Terminate daemon when set identity failure ?

* @var bool
* @since 1.0.3
*/
 
var $requireSetIdentity false;

 
/**
* Path to PID file

* @var string
* @since 1.0.1
*/
 
var $pidFileLocation '/tmp/daemon.pid';

 
/**
* Home path

* @var string
* @since 1.0
*/
 
var $homePath '/';
 
/**#@-*/


 /**#@+
* @access protected
*/
 /**
* Current process ID

* @var int
* @since 1.0
*/
 
var $_pid 0;

 
/**
* Is this process a children

* @var boolean
* @since 1.0
*/
 
var $_isChildren false;

 
/**
* Is daemon running

* @var boolean
* @since 1.0
*/
 
var $_isRunning false;
 
/**#@-*/


 /**
* Constructor
*
* @access public
* @since 1.0
* @return void
*/
 
function Daemon()
 {
error_reporting(0);
set_time_limit(0);
ob_implicit_flush();

register_shutdown_function(array(&$this'releaseDaemon'));
 }

 
/**
* Starts daemon
*
* @access public
* @since 1.0
* @return bool
*/
 
function start()
 {
$this->_logMessage('Starting daemon');

if (!
$this->_daemonize())
{
 
$this->_logMessage('Could not start daemon'DLOG_ERROR);

 return 
false;
}


$this->_logMessage('Running...');

$this->_isRunning true;


while (
$this->_isRunning)
{
 
$this->_doTask();
}

return 
true;
 }

 
/**
* Stops daemon
*
* @access public
* @since 1.0
* @return void
*/
 
function stop()
 {
$this->_logMessage('Stoping daemon');

$this->_isRunning false;
 }

 
/**
* Do task
*
* @access protected
* @since 1.0
* @return void
*/
 
function _doTask()
 {
// override this method
 
}

 
/**
* Logs message
*
* @access protected
* @since 1.0
* @return void
*/
 
function _logMessage($msg$level DLOG_NOTICE)
 {
 
// override this method
 
}

 
/**
* Daemonize
*
* Several rules or characteristics that most daemons possess:
* 1) Check is daemon already running
* 2) Fork child process
* 3) Sets identity
* 4) Make current process a session laeder
* 5) Write process ID to file
* 6) Change home path
* 7) umask(0)

* @access private
* @since 1.0
* @return void
*/
 
function _daemonize()
 {
ob_end_flush();

if (
$this->_isDaemonRunning())
{
 
// Deamon is already running. Exiting
 
return false;
}

if (!
$this->_fork())
{
 
// Coudn't fork. Exiting.
 
return false;
}

if (!
$this->_setIdentity() && $this->requireSetIdentity)
{
 
// Required identity set failed. Exiting
 
return false;
}

if (!
posix_setsid())
{
 
$this->_logMessage('Could not make the current process a session leader'DLOG_ERROR);

 return 
false;
}

if (!
$fp = @fopen($this->pidFileLocation'w'))
{
 
$this->_logMessage('Could not write to PID file'DLOG_ERROR);

 return 
false;
}
else
{
 
fputs($fp$this->_pid);
 
fclose($fp);
}

@
chdir($this->homePath);
umask(0);

declare(
ticks 1);

pcntl_signal(SIGCHLD, array(&$this'sigHandler'));
pcntl_signal(SIGTERM, array(&$this'sigHandler'));

return 
true;
 }

 
/**
* Cheks is daemon already running
*
* @access private
* @since 1.0.3
* @return bool
*/
 
function _isDaemonRunning()
 {
$oldPid = @file_get_contents($this->pidFileLocation);

if (
$oldPid !== false && posix_kill(trim($oldPid),0))
{
 
$this->_logMessage('Daemon already running with PID: '.$oldPid, (DLOG_TO_CONSOLE DLOG_ERROR));

 return 
true;
}
else
{
 return 
false;
}
 }

 
/**
* Forks process
*
* @access private
* @since 1.0
* @return bool
*/
 
function _fork()
 {
$this->_logMessage('Forking...');

$pid pcntl_fork();

if (
$pid == -1// error
{
 
$this->_logMessage('Could not fork'DLOG_ERROR);

 return 
false;
}
else if (
$pid// parent
{
 
$this->_logMessage('Killing parent');

 exit();
}
else 
// children
{
 
$this->_isChildren true;
 
$this->_pid posix_getpid();

 return 
true;
}
 }

 
/**
* Sets identity of a daemon and returns result
*
* @access private
* @since 1.0
* @return bool
*/
 
function _setIdentity()
 {
if (!
posix_setgid($this->groupID) || !posix_setuid($this->userID))
{
 
$this->_logMessage('Could not set identity'DLOG_WARNING);

 return 
false;
}
else
{
 return 
true;
}
 }

 
/**
* Signals handler
*
* @access public
* @since 1.0
* @return void
*/
 
function sigHandler($sigNo)
 {
switch (
$sigNo)
{
 case 
SIGTERM:  // Shutdown
$this->_logMessage('Shutdown signal');
exit();
break;

 case 
SIGCHLD:  // Halt
$this->_logMessage('Halt signal');
while (
pcntl_waitpid(-1$statusWNOHANG) > 0);
break;
}
 }

 
/**
* Releases daemon pid file
* This method is called on exit (destructor like)
*
* @access public
* @since 1.0
* @return void
*/
 
function releaseDaemon()
 {
if (
$this->_isChildren && file_exists($this->pidFileLocation))
{
 
$this->_logMessage('Releasing daemon');

 
unlink($this->pidFileLocation);
}
 }
}
?>
O_CONSOLE'1);
define('DLOG_NOTICE'2);
define('DLOG_WARNING'4);
define('DLOG_ERROR'8);
define('DLOG_CRITICAL'16);

/**
 * Daemon base class
 *
 * Requirements:
 * Unix like operating system
 * php 4 >= 4.3.0 or php 5
 * php compiled with:
 * --enable-sigchild
 * --enable-pcntl
 *
 * @package binarychoice.system.unix
 * @author Michal 'Seth' Golebiowski <seth at binarychoice dot pl>
 * @copyright Copyright 2005 Seth
 * @since 1.0.3
 */
class Daemon
{
 
/**#@+
* @access public
*/
 /**
* User ID

* @var int
* @since 1.0
*/
 
var $userID 99;

 
/**
* Group ID

* @var integer
* @since 1.0
*/
 
var $groupID 99;
 
 
/**
* Terminate daemon when set identity failure ?

* @var bool
* @since 1.0.3
*/
 
var $requireSetIdentity false;

 
/**
* Path to PID file

* @var string
* @since 1.0.1
*/
 
var $pidFileLocation '/tmp/daemon.pid';

 
/**
* Home path

* @var string
* @since 1.0
*/
 
var $homePath '/';
 
/**#@-*/


 /**#@+
* @access protected
*/
 /**
* Current process ID

* @var int
* @since 1.0
*/
 
var $_pid 0;

 
/**
* Is this process a children

* @var boolean
* @since 1.0
*/
 
var $_isChildren false;

 
/**
* Is daemon running

* @var boolean
* @since 1.0
*/
 
var $_isRunning false;
 
/**#@-*/


 /**
* Constructor
*
* @access public
* @since 1.0
* @return void
*/
 
function Daemon()
 {
error_reporting(0);
set_time_limit(0);
ob_implicit_flush();

register_shutdown_function(array(&$this'releaseDaemon'));
 }

 
/**
* Starts daemon
*
* @access public
* @since 1.0
* @return bool
*/
 
function start()
 {
$this->_logMessage('Starting daemon');

if (!
$this->_daemonize())
{
 
$this->_logMessage('Could not start daemon'DLOG_ERROR);

 return 
false;
}


$this->_logMessage('Running...');

$this->_isRunning true;


while (
$this->_isRunning)
{
 
$this->_doTask();
}

return 
true;
 }

 
/**
* Stops daemon
*
* @access public
* @since 1.0
* @return void
*/
 
function stop()
 {
$this->_logMessage('Stoping daemon');

$this->_isRunning false;
 }

 
/**
* Do task
*
* @access protected
* @since 1.0
* @return void
*/
 
function _doTask()
 {
// override this method
 
}

 
/**
* Logs message
*
* @access protected
* @since 1.0
* @return void
*/
 
function _logMessage($msg$level DLOG_NOTICE)
 {
 
// override this method
 
}

 
/**
* Daemonize
*
* Several rules or characteristics that most daemons possess:
* 1) Check is daemon already running
* 2) Fork child process
* 3) Sets identity
* 4) Make current process a session laeder
* 5) Write process ID to file
* 6) Change home path
* 7) umask(0)

* @access private
* @since 1.0
* @return void
*/
 
function _daemonize()
 {
ob_end_flush();

if (
$this->_isDaemonRunning())
{
 
// Deamon is already running. Exiting
 
return false;
}

if (!
$this->_fork())
{
 
// Coudn't fork. Exiting.
 
return false;
}

if (!
$this->_setIdentity() && $this->requireSetIdentity)
{
 
// Required identity set failed. Exiting
 
return false;
}

if (!
posix_setsid())
{
 
$this->_logMessage('Could not make the current process a session leader'DLOG_ERROR);

 return 
false;
}

if (!
$fp = @fopen($this->pidFileLocation'w'))
{
 
$this->_logMessage('Could not write to PID file'DLOG_ERROR);

 return 
false;
}
else
{
 
fputs($fp$this->_pid);
 
fclose($fp);
}

@
chdir($this->homePath);
umask(0);

declare(
ticks 1);

pcntl_signal(SIGCHLD, array(&$this'sigHandler'));
pcntl_signal(SIGTERM, array(&$this'sigHandler'));

return 
true;
 }

 
/**
* Cheks is daemon already running
*
* @access private
* @since 1.0.3
* @return bool
*/
 
function _isDaemonRunning()
 {
$oldPid = @file_get_contents($this->pidFileLocation);

if (
$oldPid !== false && posix_kill(trim($oldPid),0))
{
 
$this->_logMessage('Daemon already running with PID: '.$oldPid, (DLOG_TO_CONSOLE DLOG_ERROR));

 return 
true;
}
else
{
 return 
false;
}
 }

 
/**
* Forks process
*
* @access private
* @since 1.0
* @return bool
*/
 
function _fork()
 {
$this->_logMessage('Forking...');

$pid pcntl_fork();

if (
$pid == -1// error
{
 
$this->_logMessage('Could not fork'DLOG_ERROR);

 return 
false;
}
else if (
$pid// parent
{
 
$this->_logMessage('Killing parent');

 exit();
}
else 
// children
{
 
$this->_isChildren true;
 
$this->_pid posix_getpid();

 return 
true;
}
 }

 
/**
* Sets identity of a daemon and returns result
*
* @access private
* @since 1.0
* @return bool
*/
 
function _setIdentity()
 {
if (!
posix_setgid($this->groupID) || !posix_setuid($this->userID))
{
 
$this->_logMessage('Could not set identity'DLOG_WARNING);

 return 
false;
}
else
{
 return 
true;
}
 }

 
/**
* Signals handler
*
* @access public
* @since 1.0
* @return void
*/
 
function sigHandler($sigNo)
 {
switch (
$sigNo)
{
 case 
SIGTERM:  // Shutdown
$this->_logMessage('Shutdown signal');
exit();
break;

 case 
SIGCHLD:  // Halt
$this->_logMessage('Halt signal');
while (
pcntl_waitpid(-1$statusWNOHANG) > 0);
break;
}
 }

 
/**
* Releases daemon pid file
* This method is called on exit (destructor like)
*
* @access public
* @since 1.0
* @return void
*/
 
function releaseDaemon()
 {
if (
$this->_isChildren && file_exists($this->pidFileLocation))
{
 
$this->_logMessage('Releasing daemon');

 
unlink($this->pidFileLocation);
}
 }
}
?>
pobierz, plaintext
  1. <?php
  2. class TestDaemon extends Daemon
  3. {
  4.  function TestDaemon()
  5.  {
  6. parent::Daemon();
  7.  
  8. $fp = fopen('/tmp/daemon.log', 'a');
  9. fclose($fp);
  10.  
  11. chmod('/tmp/daemon.log', 0777);
  12.  }
  13.  
  14.  function _logMessage($msg, $status = DLOG_NOTICE)
  15.  {
  16. if ($status & DLOG_TO_CONSOLE)
  17. {
  18.  print $msg."\n";
  19. }
  20.  
  21. $fp = fopen('/tmp/daemon.log', 'a');
  22. fwrite($fp, date("Y/m/d H:i:s ").$msg."\n");
  23. fclose($fp);
  24.  }
  25.  
  26.  function _doTask()
  27.  {
  28. static $i = 0;
  29.  
  30. sleep(1);
  31.  
  32. $i++;
  33.  
  34. if ($i >= 30)
  35. {
  36.  $this->stop();
  37. }
  38.  }
  39.  
  40. }
  41. ?>

Klasa TestDaemon rozszeza podstawowa klase Daemon i nadpisuje dwie metody: _doTask() i _logMessage().

_doTask() - jest caly czas uruchamiana w petli przez demona.
W przykladzie TestDaemon w metodzie tej utworzylem zmienna statyczna $i, ktora nie zmienia swojej wartosci po wyjsciu z funkcji.
sleep(1) posluzyl mi do opoznienia dzialania znajdujacego sie dalej kodu o jedna sekunde.
Jezeli nie ustawimy chociaz usleep(100) [albo mniej] - czyli 1 dziesiatej sekundy - wtedy zuzycie procesora bedzie bliskie 99% gdyz demon caly czas wykonuje jedna funkcje w petli, a ta moze sie wykonac tysiace razy w ciagu jednej sekundy, w zaleznosci od szybkosci sprzetu.
Jako, ze nie potrzebujemy tylu wywolan tej funkcji podczas jednej sekundy dajemy opoznienie.
W dalszej czesci metody widac, ze do momentu az $i nie osiagnie 30 dalej pozwalamy demonowi dzialac.
W efekcie po 30 sekundach zostanie zamkniety daemon.

_logMessage() - sluzy nam do logowania zdarzen klasy Deamon


Potrzebny bedzie nam jeszcze jeden plik, ktory odpali nam caly przyklad:
  1. <?php
  2. require_once ('Daemon.class.php');
  3. require_once ('TestDaemon.class.php');
  4.  
  5. $Daemon = new TestDaemon();
  6. $Daemon->start();
  7. ?>

Zauwazcie, ze po jego uruchomieniu odrazu wrocimy do obszaru wpisywania polecen w konsoli. Tak sie dzieje gdyz podczas rozdzielenia procesu na dwa (dwa identyczne rozniace sie numerkami procesu), nastepuje zamkniecie procesu rodzica - czyli tego, ktory zostal uruchomiony spod konsoli - a proces dziecka, nadal dziala w tle.

Caly proces dzialania mozemy przesledzic czytajac logi demona.
Najlatwiej bedzie nam na bierzaco odczytywac plik daemon.log przy uzyciu komendy tail w wierszu polecen:
Kod
tail -f /tmp/daemon.log



I to wzasadzie tyle. Teraz tylko trzeba wymyslic cos ciekawego i zaprzasc demona do pracy. Mysle, ze mozna by nawet pokusic sie o napisanie wlasnego serwera WWW w php wlasnie w oparciu o ta klase.

Ten post edytował Seth 7.02.2006, 17:01:42
Go to the top of the page
+Quote Post

Posty w temacie
- Seth   Demon UNIXowy w php   7.02.2006, 16:53:58
- - ActivePlayer   ja odnosnie etapów. Cytat    * Rozwidlic...   7.02.2006, 18:45:54
- - Seth   Jak najbardziej mogl by dzialac w petli (zreszta i...   7.02.2006, 21:10:49
- - LBO   hmmm.. a moze kilka słów o zastosowaniach... ?.. ...   23.02.2006, 17:03:56
- - slash.   co do tematu demona w srodowisku windows, cytujac ...   25.02.2006, 12:22:49

« Następny starszy · Archiwum Pro · Następny nowszy »
 

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 24.12.2025 - 16:07