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
 
Start new topic
Odpowiedzi
ActivePlayer
post
Post #2





Grupa: Przyjaciele php.pl
Postów: 1 224
Pomógł: 40
Dołączył: 6.07.2004
Skąd: Wuppertal

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


ja odnosnie etapów.
Cytat
    * Rozwidlic procesy - utworzyc proces dziecka (fork), a proces rodzica zamknac. A przed tym zamknac jakiekolwiek polaczenia do pliku gdyz moze to prowadzic co nadpisania danych procesu rodzica lub dziecka.

Nigdy nie zajmowalem sie tym wczesniej, wiec zastanawiam sie po co ? demon nie moglby dzialac w petli i poprostu sprawdzac czy jest cos 'todo' ? no chyba ze to jest jakas forma zapewnienia wielowątkowosci? dlaczego wiec zamykac proces rodzica?

Co do wykozystania... pierwsze na mysl przyszlo mi 'cron', na podstawie tej klasy mozna by napisac alternatywe crona. drugie o czym pomyslalem to implementacja pod windowsami.
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: 25.12.2025 - 21:57