用php写的一个daemon程序

本文介绍了一个PHP守护进程的基础实现方式,包括如何使进程后台运行、处理信号、记录日志等关键功能,并提供了一个完整的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础类

<?php
/**
* @package binarychoice.system.unix
* @since 1.0.3
*/

// Log message levels
define('DLOG_TO_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 = '/var/run/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' . "[$this->pidFileLocation]", 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, $status, WNOHANG) > 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);
       }
    }
}
?>

使用例子

<?php
class TestDaemon extends Daemon
{
    function TestDaemon()
    {
       parent::Daemon();

       $fp = fopen('/tmp/daemon.log', 'a');
       fclose($fp);
   
       chmod('/tmp/daemon.log', 0777);
    }

    function _logMessage($msg, $status = DLOG_NOTICE)
    {
       if ($status & DLOG_TO_CONSOLE)
       {
          print $msg."/n";
       }
      
       $fp = fopen('/tmp/daemon.log', 'a');
       fwrite($fp, date("Y/m/d H:i:s ").$msg."/n");
       fclose($fp);
    }

    function _doTask()
    {
       static $i = 0;
   
       sleep(1);
       echo $i;
   
       $i++;
   
       if ($i >= 30)
       {
          $this->stop();
       }
    }

}
?>

 

<?php
require_once ('Daemon.class.php');
require_once ('TestDaemon.class.php');

$Daemon = new TestDaemon();
$Daemon->start();
?>

 

 

 

 

原文地址:http://hi.baidu.com/titanictom/blog/item/ed2459db042b3564d1164eac.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值