PHP守护进程类 -- KalonDaemon

    本文地址:http://tongxinmao.com/Article/Detail/id/14

    守护进程也称精灵进程(daemon),是生存期较长的一种进程。它们常常用在系统自举时启动,仅在系统关闭时才终止。因为它们没有控制终端,所以说它们是在后台运行的。UNIX类操作系统有很多的守护进程,它们执行日常事务活动。

    目前有大量的web站点基与PHP开发,业务逻辑都是由PHP来实现,很多时候我们也需要一个PHP的daemon来做一些日常事务,例如我们想每隔一个小时统计一下数据库中的某项数据,每天定期的执行一些备份或则监控任务。这些任务在apache模块的web环境下实现比较困难而且容易引发很多问题。
    这里我介绍一款我自己写的PHP5版的daemon类 - KalonDaemon.   ^_^  现在和大家一起分享。

    概要:
        KalonDaemon是一款PHP5的daemon类,我们在PHP代码中可以直接包含并且使用,KalonDaemon工作在cli sapi下( command line interface),它能把一个普通的PHP进程变成一个守护进程。
    使用方式:
      在PHP脚本中包含了KalonDaemon设置好参数然后调用start()方法。然后我们在命令行下用PHP cli执行脚本,比如cli sapi路径为 /usr/local/bin/php,   我们编写的程序路径 /home/test/mydaemon.php,那么我们用以下方式运行程序:          /usr/local/bin/php  /home/test/mydaemon.php   根据需要可以在后面添加别的参数。
    工作流程:
    KalonDaemon遵循大部分unix类系统下的守护进程编程规则,主要工作流程如下:
    1. 调用pcntl_fork,然后使父进程退出(exit).这样做实现如下几点:第一,如果该守护进程是作为一条shell命令启动,那么父进程终止使得 shell认为这条命令已经执行完毕;第二,子进程继承父进程的进程组ID,但是具有一个新的进程ID,这就保证了子进程不是一个进程组的组长,这对于下面要做的posix_setsid调用是必要的前提条件。
    2.调用posix_setsid以创建一个新的会话,这样新进程就成为了新会话的首进程,同时是新进程组的组长进程,而且没有控制终端。
    3.设置进程信号回调函数,方便我们用其它进程对守护进程进行控制。

     

    以下是mydaemon.php的源码:


     


    [php] view plaincopy

    1. <?php  

    2. require_once './KalonDaemon.php';  

    3. declare(ticks = 1);  

    4. $toDo = $_SERVER['argv'][1];  

    5. $daemonConf = array('pidFileName' => 'mydaemon.pid',  

    6.                     'verbose'     => true);  

    7. function myHandler1()  

    8. {  

    9.     sleep(5);  

    10.     echo "This handler1 works./n";  

    11. }  

    12. function myHandler2()  

    13. {  

    14.    echo "This handler2 works./n";  

    15. }  

    16. try {  

    17.     $daemon = new KalonDaemon($daemonConf);  

    18.     if ($toDo == 'start') {  

    19.         $daemon->addSignalHandler(SIGUSR1, 'myHandler1');  

    20.         $daemon->addSignalHandler(SIGUSR2, 'myHandler2');  

    21.         $daemon->start();  

    22.         for (;;) {  

    23.             echo "running./n";  

    24.             sleep(1000);  

    25.         }  

    26.     } elseif ($toDo == 'stop') {  

    27.         $daemon->stop();  

    28.     } else {  

    29.         die("unknown action.");  

    30.     }  

    31. } catch (KalonDaemonException $e) {  

    32.     echo $e->getMessage();  

    33.     echo "/n";  

    34. }  

    35. ?>  



     

    在命令行下执行:

    /path/to/phpcli/php  mydaemon.php start

     

    输出如下信息:

    Daemon started with pid 8976...
    running.

    说明守护进程已经开始运行,进程号为8976,当然一般情况进程号每次都会不一样。

     

    由于mydaemon.php中有一个死循环,每次循环会睡眠1000秒,所以进程永远不会终止。

    mydaemon.php中为守护进程注册了两个信号句柄,信号SIGUSR1对应函数myHandler1(), 信号SIGUSR2对应myHandler2(),我们可以通过kill命令给进程发送这两个信号来唤醒进程。

     

    kill -SIGUSR2 8976

    输出信息如下:

    This handler2 works.
    running.

    说明睡眠中的进程被唤醒,并且执行了myHandler2()函数,然后再次进入了循环。

     

     

    当我们需要终止守护进程的时候,可以用以下命令:

    /path/to/phpcli/php  mydaemon.php stop

    输出信息如下:

    Daemon stopped with pid 8976...

    这样守护进程就终止了。

     

    这样的特性可以在某些应用场景非常有用,比如服务器在接受到一些上传的数据之后,需要唤醒守护进程来处理这些数据。守护进程可以长期出去睡眠状态等待,当数据到来之后,发送信号唤醒守护进程,守护进程马上开始处理这些数据。这样要比定期的轮询效率高很多,而且不会有延迟现象。

     

     

    KalonDaemon.php


    [php] view plaincopy

    1. <?php  

    2. /** 

    3.  * Kalon Daemon -> A Unix Daemon for PHP5 

    4.  *     This is a free daemon tool, you can use it anyway you like. 

    5.  *  

    6.  * NOTICE: 

    7.  * 1:This tool must run in cli sapi, any other sapis will cause a  

    8.  *   KalonDaemonException thrown.so you need to use this tool in a 

    9.  *   command line interface,command such as: /path/to/php mydaemon.php 

    10.  *  

    11.  * 2:Daemon needs pcntl and posix extension support. Make sure your cli 

    12.  *   sapi has loaded these two extension.The posix is compiled in php by 

    13.  *   default, while pcntl must be compiled or dynamic load by yourself. 

    14.  *   Missing anyone of these extension will cause a KalonDaemonException  

    15.  *   thrown. 

    16.  *  

    17.  * USAGE: 

    18.  *  

    19.  *put the code below in mydaemon.php  

    20.  *  

    21. require_once '/path/to/KalonDaemon.php'; 

    22. declare(ticks = 1); 

    23. $toDo = $_SERVER['argv'][1]; 

    24. $daemonConf = array('pidFileName' => 'mydaemon.pid', 

    25.                     'verbose'     => true); 

    26. function myHandler1() 

    27. { 

    28.     sleep(5); 

    29.     echo "This handler1 works./n"; 

    30. } 

    31. function myHandler2() 

    32. { 

    33.    echo "This handler2 works./n"; 

    34. } 

    35. try { 

    36.     $daemon = new KalonDaemon($daemonConf); 

    37.     if ($toDo == 'start') { 

    38.         $daemon->addSignalHandler(SIGUSR1, 'myHandler1'); 

    39.         $daemon->addSignalHandler(SIGUSR2, 'myHandler2'); 

    40.         $daemon->start(); 

    41.         for (;;) { 

    42.             echo "running./n"; 

    43.             sleep(1000); 

    44.         } 

    45.     } elseif ($toDo == 'stop') { 

    46.         $daemon->stop(); 

    47.     } else { 

    48.         die("unknown action."); 

    49.     } 

    50. } catch (KalonDaemonException $e) { 

    51.     echo $e->getMessage(); 

    52.     echo "/n"; 

    53. } 

    54.  *  

    55.  * then open a command shell: 

    56.  * start daemon: 

    57.  * /path/to/phpcli/php /path/to/mydaemon.php start 

    58.  *  

    59.  * stop daemon: 

    60.  * /path/to/phpcli/php /path/to/mydaemon.php stop 

    61.  *  

    62.  *  

    63.  *  

    64.  * @author 玉面修罗  - Kalon 

    65.  * @version 1.0 

    66.  * @site: http://blog.csdn.net/phpkernel 

    67.  * E-mail/MSN: xiuluo-999@163.com 

    68.  */  

    69.   

    70.   

    71. class KalonDaemon   

    72. {  

    73.     /** 

    74.      * path of pid file 

    75.      * 

    76.      * @var string 

    77.      */  

    78.     private $_pidFilePath = "/var/run";  

    79.       

    80.     /** 

    81.      * name of pid file 

    82.      * 

    83.      * @var string 

    84.      */  

    85.     private $_pidFileName = "daemon.pid";  

    86.       

    87.     /** 

    88.      * out put run information 

    89.      * 

    90.      * @var boolean 

    91.      */  

    92.     private $_verbose = false;  

    93.       

    94.     /** 

    95.      * default singleton model 

    96.      * 

    97.      * @var boolean 

    98.      */  

    99.     private $_singleton = true;   

    100.       

    101.     /** 

    102.      * close file handle STDIN STDOUT STDERR 

    103.      * NOTICE: we do not close STDIN STDOUT STDERR indeed for some reason.  

    104.      * @var boolean 

    105.      */  

    106.     private $_closeStdHandle = true;  

    107.       

    108.     /** 

    109.      * pid of daemon 

    110.      * 

    111.      * @var int 

    112.      */  

    113.     private $_pid = 0;  

    114.       

    115.     /** 

    116.      * exec file  

    117.      * 

    118.      * @var string 

    119.      */  

    120.     private $_execFile = "";  

    121.       

    122.   

    123.     /** 

    124.      * function handlers for signal number 

    125.      * 

    126.      * @var array 

    127.      */  

    128.     private $_signalHandlerFuns = array();  

    129.   

    130.       

    131.     /** 

    132.      * set config 

    133.      * 

    134.      * @param array $configs 

    135.      */  

    136.     public function __construct($configs = array())  

    137.     {     

    138.         //load config  

    139.         if (is_array($configs))  

    140.             $this->setConfigs($configs);  

    141.     }  

    142.       

    143.     /** 

    144.      * pctntl is needed,and only works in cli sapi 

    145.      */  

    146.     public function _checkRequirement()  

    147.     {  

    148.         //check if pctnl loaded  

    149.         if (!extension_loaded('pcntl'))  

    150.             throw new KalonDaemonException("daemon needs support of pcntl extension, please enable it.");  

    151.   

    152.         //check sapi name,only for cli      

    153.         if ('cli' != php_sapi_name())  

    154.             throw new KalonDaemonException("daemon only works in cli sapi.");      

    155.     }  

    156.       

    157.     /** 

    158.      * set configs 

    159.      * pidFilePath: path of pid file 

    160.      * pidFileName: name of pid file 

    161.      * verbose    : output process information 

    162.      * singleton  : singleton model,only one instance of daemon at one time 

    163.      * closeStdHandle : close STDIN STDOUT STDERR when daemon run success 

    164.      *  

    165.      * @param array $configs 

    166.      */  

    167.     public function setConfigs($configs)  

    168.     {  

    169.         foreach ((array$configs as $item => $config) {  

    170.             switch ($item) {  

    171.                 case "pidFilePath":  

    172.                     $this->setPidFilePath($config);  

    173.                     break;  

    174.                 case "pidFileName":  

    175.                     $this->setPidFileName($config);  

    176.                     break;  

    177.                  case "verbose":  

    178.                     $this->setVerbose($config);  

    179.                     break;  

    180.                  case "singleton":  

    181.                     $this->setSingleton($config);  

    182.                     break;  

    183.                  case "closeStdHandle";  

    184.                     $this->setCloseStdHandle($config);   

    185.                     break;                                          

    186.                 default:  

    187.                     throw new KalonDaemonException("Unknown config item {$item}");  

    188.                     break;  

    189.             }  

    190.         }  

    191.     }  

    192.       

    193.     /** 

    194.      * set Pid File Path 

    195.      * 

    196.      * @param  string $path 

    197.      * @return boolean 

    198.      */  

    199.     public function setPidFilePath($path)  

    200.     {  

    201.         if (empty($path))  

    202.             return false;  

    203.               

    204.         if(!is_dir($path))  

    205.             if (!mkdir($path, 0777))  

    206.                 throw new KalonDaemonException("setPidFilePath: cannnot make dir {$path}.");  

    207.   

    208.         $this->_pidFilePath = rtrim($path"/");  

    209.         return true;      

    210.     }  

    211.       

    212.     /** 

    213.      * get Pid File Path 

    214.      * 

    215.      * @return string 

    216.      */  

    217.     public function getPidFilePath()  

    218.     {  

    219.         return $this->_pidFilePath;  

    220.     }  

    221.       

    222.     /** 

    223.      * set Pid File Name 

    224.      * 

    225.      * @param string $name 

    226.      * @return boolean 

    227.      */  

    228.     public function setPidFileName($name)  

    229.     {  

    230.         if (empty($name))  

    231.             return false;  

    232.           

    233.         $this->_pidFileName = trim($name);  

    234.         return true;      

    235.     }  

    236.       

    237.     /** 

    238.      * get Pid File Name 

    239.      * 

    240.      * @return string 

    241.      */  

    242.     public function getPidFileName()  

    243.     {  

    244.         return $this->_pidFileName;  

    245.     }  

    246.       

    247.     /** 

    248.      * set Open Output 

    249.      *    if sets to true,daemon will output start and stop information ,etc 

    250.      *  

    251.      * @param  boolean $open 

    252.      * @return boolean 

    253.      */  

    254.     public function setVerbose($open = true)  

    255.     {  

    256.         $this->_verbose = (boolean) $open;  

    257.         return true;  

    258.     }  

    259.       

    260.     /** 

    261.      * get Open Output 

    262.      * 

    263.      * @return boolean 

    264.      */  

    265.     public function getVerbose()  

    266.     {  

    267.         return $this->_verbose;  

    268.     }  

    269.       

    270.     /** 

    271.      * set Singleton 

    272.      *     if sets to true, daemon will keep singleton,which means that there is only one  

    273.      * instance of daemon at one time.     

    274.      *  

    275.      * @param  boolean $singleton 

    276.      * @return boolean 

    277.      */  

    278.     public function setSingleton($singleton = true)  

    279.     {  

    280.         $this->_singleton = (boolean) $singleton;  

    281.         return true;  

    282.     }  

    283.       

    284.     /** 

    285.      * get Singleton 

    286.      * 

    287.      * @return boolean 

    288.      */  

    289.     public function getSingleton()  

    290.     {  

    291.         return $this->_singleton;  

    292.     }  

    293.       

    294.     /** 

    295.      * set Close Std Handle 

    296.      * 

    297.      * @param  boolean $close 

    298.      * @return boolean 

    299.      */  

    300.     public function setCloseStdHandle($close = true)  

    301.     {  

    302.         $this->_closeStdHandle = (boolean) $close;  

    303.         return true;  

    304.     }  

    305.       

    306.     /** 

    307.      * get Close Std Handle 

    308.      * 

    309.      * @return boolean 

    310.      */  

    311.     public function getCloseStdHandle()  

    312.     {  

    313.         return $this->_closeStdHandle;  

    314.     }  

    315.       

    316.     /** 

    317.      * start daemon 

    318.      * 1.daemonize  

    319.      * 2.setup signal handlers 

    320.      * 3.close STDIN STDOUT STDERR 

    321.      *  

    322.      * @return boolean 

    323.      */  

    324.     public function start()  

    325.     {  

    326.         //this line used to put in the __construct,for some reason I move it here.  

    327.         $this->_checkRequirement();  

    328.           

    329.         //do daemon  

    330.         $this->_daemonize();  

    331.    

    332.         //default handler for stop  

    333.         if(!pcntl_signal(SIGTERM,  array($this,"signalHandler")))  

    334.             throw new KalonDaemonException("Cannot setup signal handler for signo {$signo}");       

    335.           

    336.           

    337.         //close file handle STDIN STDOUT STDERR  

    338.         //notic!!!This makes no use in PHP4 and some early version of PHP5  

    339.         //if we close these handle without dup to /dev/null,php process will die   

    340.         //when operating on them.  

    341.         if ($this->_closeStdHandle) {  

    342.             //fclose(STDIN);  

    343.             //fclose(STDOUT);  

    344.             //fclose(STDERR);  

    345.         }  

    346.           

    347.         return true;  

    348.     }  

    349.       

    350.     /** 

    351.      * stop daemon 

    352.      * 1.get daemon pid from pid file 

    353.      * 2.send signal to daemon 

    354.      *  

    355.      * @param  boolean $force  kill -9 or kill 

    356.      * @return boolean 

    357.      */  

    358.     public function stop($force = false)  

    359.     {  

    360.         if ($force)   

    361.             $signo = SIGKILL; //kill -9  

    362.         else    

    363.             $signo = SIGTERM; //kill   

    364.                   

    365.         //only use in singleton model      

    366.         if (!$this->_singleton)  

    367.             throw new KalonDaemonException("'stop' only use in singleton model.");  

    368.                      

    369.         if (false === ($pid = $this->_getPidFromFile()))  

    370.             throw new KalonDaemonException("daemon is not running,cannot stop.");  

    371.           

    372.         if (!posix_kill($pid$signo)) {  

    373.             throw new KalonDaemonException("Cannot send signal $signo to daemon.");   

    374.         }  

    375.           

    376.         $this->_unlinkPidFile();  

    377.           

    378.         $this->_out("Daemon stopped with pid {$pid}...");  

    379.         return true;  

    380.     }  

    381.       

    382.     /** 

    383.      * restart daemon 

    384.      */  

    385.     public function restart()  

    386.     {  

    387.         $this->stop();  

    388.         //sleep to wait  

    389.         sleep(1);  

    390.           

    391.         $this->start();  

    392.     }  

    393.           

    394.     /** 

    395.      * get daemon pid 

    396.      * @return int 

    397.      */  

    398.     public function getDaemonPid()  

    399.     {  

    400.         return $this->_getPidFromFile();  

    401.     }  

    402.       

    403.     /** 

    404.      * signalHander for dameon 

    405.      * 

    406.      * @param int $signo 

    407.      */  

    408.     public function signalHandler($signo)  

    409.     {     

    410.         $signFuns = $this->_signalHandlerFuns[$signo];  

    411.         if (is_array($signFuns)) {  

    412.             foreach ($signFuns as $fun) {  

    413.                 call_user_func($fun);  

    414.             }  

    415.         }  

    416.           

    417.         //default action  

    418.         switch ($signo) {  

    419.             case SIGTERM:  

    420.                 exit;  

    421.                 break;  

    422.             default:  

    423.                 // handle all other signals  

    424.         }         

    425.           

    426.     }  

    427.       

    428.     public function addSignalHandler($signo$fun)  

    429.     {  

    430.         if (is_string($fun)) {  

    431.             if (!function_exists($fun)) {  

    432.                 throw new KalonDaemonException("handler function {$fun} not exists");  

    433.             }  

    434.         }elseif (is_array($fun)) {  

    435.             if (!@method_exists($fun[0], $fun[1])) {  

    436.                 throw new KalonDaemonException("handler method not exists");  

    437.             }      

    438.         } else {  

    439.             throw new KalonDaemonException("error handler.");  

    440.         }  

    441.   

    442.         if(!pcntl_signal($signo,  array($this,"signalHandler")))  

    443.                 throw new KalonDaemonException("Cannot setup signal handler for signo {$signo}");  

    444.   

    445.         $this->_signalHandlerFuns[$signo][] = $fun;  

    446.         return $this;      

    447.     }  

    448.       

    449.     public function sendSignal($signo)  

    450.     {  

    451.         if (false === ($pid = $this->_getPidFromFile()))  

    452.             throw new KalonDaemonException("daemon is not running,cannot send signal.");  

    453.           

    454.         if (!posix_kill($pid$signo)) {  

    455.             throw new KalonDaemonException("Cannot send signal $signo to daemon.");   

    456.         }  

    457.           

    458.         //$this->_out("Send signal $signo to pid $pid...");  

    459.         return true;  

    460.     }  

    461.       

    462.     /** 

    463.      * daemon is active? 

    464.      * @return boolean 

    465.      */  

    466.     public function isActive()  

    467.     {  

    468.         try {  

    469.             $pid = $this->_getPidFromFile();  

    470.         } catch (KalonDaemonException $e) {  

    471.             return false;  

    472.         }  

    473.         if (false === $pid)  

    474.             return false;  

    475.               

    476.         if (false === ($active = @pcntl_getpriority($pid)))  

    477.             return false;  

    478.         else  

    479.             return true;  

    480.     }  

    481.       

    482.       

    483.     /** 

    484.      * daemonize  

    485.      * 1.check running , if singaleton model 

    486.      * 2.forck process 

    487.      * 3.detach from controlling terminal 

    488.      * 4.log pid 

    489.      *  

    490.      * @return boolean 

    491.      */  

    492.     private function _daemonize()  

    493.     {  

    494.         //single model, first check if running  

    495.         if ($this->_singleton) {  

    496.             $isRunning  = $this->_checkRunning();  

    497.             if ($isRunning)   

    498.                 throw new KalonDaemonException("Daemon already running");  

    499.         }  

    500.           

    501.         //fork current process  

    502.         $pid = pcntl_fork();  

    503.           

    504.         if ($pid == -1) {  

    505.             //fork error  

    506.             throw new KalonDaemonException("Error happened while fork process");  

    507.         } elseif ($pid) {  

    508.             //parent exit  

    509.             exit();  

    510.         } else {  

    511.             //child, get pid  

    512.             $this->_pid = posix_getpid();  

    513.         }  

    514.           

    515.         $this->_out("Daemon started with pid {$this->_pid}...");  

    516.           

    517.         //detach from controlling terminal  

    518.         if (!posix_setsid())  

    519.             throw new KalonDaemonException("Cannot detach from terminal");   

    520.           

    521.         //log pid in singleton model      

    522.         if ($this->_singleton)     

    523.             $this->_logPid();  

    524.           

    525.         return $this->_pid;  

    526.     }  

    527.       

    528.     /** 

    529.      * get Pid From File 

    530.      * 

    531.      * @return int 

    532.      */  

    533.     private function _getPidFromFile()  

    534.     {  

    535.         //if is set  

    536.         if ($this->_pid)  

    537.             return (int)$this->_pid;  

    538.               

    539.         $pidFile = $this->_pidFilePath . "/" . $this->_pidFileName;  

    540.         //no pid file,it's the first time of running  

    541.         if (!file_exists($pidFile))  

    542.             return false;  

    543.               

    544.         if (!$handle = fopen($pidFile"r"))   

    545.             throw new KalonDaemonException("Cannot open pid file {$pidFile} for read");   

    546.   

    547.         if (($pid = fread($handle, 1024)) === false)   

    548.             throw new KalonDaemonException("Cannot read from pid file {$pidFile}");   

    549.       

    550.         fclose($handle);  

    551.           

    552.         return $this->_pid = (int) $pid;  

    553.     }  

    554.       

    555.     /** 

    556.      * _checkRunning 

    557.      *  in singleton mode ,we check if daemon running 

    558.      *  

    559.      * @return boolean 

    560.      */  

    561.     private function _checkRunning()  

    562.     {  

    563.         $pid = $this->_getPidFromFile();  

    564.           

    565.         //no pid file,not running  

    566.         if(false === $pid)  

    567.             return false;  

    568.           

    569.         //get exe file path from pid  

    570.         switch(strtolower(PHP_OS))  

    571.         {  

    572.             case "freebsd":  

    573.                 $strExe = $this->_getFreebsdProcExe($pid);  

    574.                 if($strExe === false)  

    575.                     return false;  

    576.                 $strArgs = $this->_getFreebsdProcArgs($pid);  

    577.                 break;  

    578.                   

    579.             case "linux":  

    580.                 $strExe = $this->_getLinuxProcExe($pid);  

    581.                 if($strExe === false)  

    582.                     return false;  

    583.                 $strArgs = $this->_getLinuxProcArgs($pid);  

    584.                 break;  

    585.                   

    586.             default:  

    587.                 return false;  

    588.         }  

    589.           

    590.         $exeRealPath = $this->_getDaemonRealPath($strArgs$pid);  

    591.           

    592.         //get exe file path from command  

    593.         if ($strExe != PHP_BINDIR . "/php")  

    594.             return false;  

    595.            

    596.         $selfFile = "";  

    597.         $sapi = php_sapi_name();  

    598.         switch($sapi)  

    599.         {  

    600.             case "cgi":  

    601.             case "cgi-fcgi":  

    602.                 $selfFile = $_SERVER['argv'][0];  

    603.                 break;  

    604.             default:  

    605.                 $selfFile = $_SERVER['PHP_SELF'];  

    606.                 break;  

    607.         }  

    608.         $currentRealPath = realpath($selfFile);  

    609.           

    610.           

    611.         //compare two path  

    612.         if ($currentRealPath != $exeRealPath)  

    613.             return false;  

    614.         else   

    615.             return true;  

    616.     }  

    617.       

    618.     /** 

    619.      * log Pid 

    620.      */  

    621.     private function _logPid()  

    622.     {  

    623.         $pidFile = $this->_pidFilePath . "/" . $this->_pidFileName;  

    624.         if (!$handle = fopen($pidFile"w")) {  

    625.             throw new KalonDaemonException("Cannot open pid file {$pidFile} for write");   

    626.         }  

    627.         if (fwrite($handle$this->_pid) == false) {  

    628.             throw new KalonDaemonException("Cannot write to pid file {$pidFile}");   

    629.         }  

    630.         fclose($handle);  

    631.     }  

    632.       

    633.     /** 

    634.      * unlink pid file 

    635.      *    in singleton mode, unlink pid file while daemon stop 

    636.      *  

    637.      * @return boolean 

    638.      */  

    639.     private function _unlinkPidFile()  

    640.     {  

    641.         $pidFile = $this->_pidFilePath . '/' . $this->_pidFileName;  

    642.         return @unlink($pidFile);  

    643.     }  

    644.       

    645.     /** 

    646.      * get Daemon RealPath 

    647.      * 

    648.      * @param string $daemonFile 

    649.      * @param int    $daemonPid 

    650.      * @return string 

    651.      */  

    652.     private function _getDaemonRealPath($daemonFile$daemonPid)  

    653.     {  

    654.         $daemonFile = trim($daemonFile);  

    655.         if(substr($daemonFile,0,1) !== "/") {  

    656.             $cwd = $this->_getLinuxProcCwd($daemonPid);  

    657.             $cwd = rtrim($cwd"/");  

    658.             $cwd = $cwd . "/" . $daemonFile;  

    659.             $cwd = realpath($cwd);  

    660.             return $cwd;  

    661.         }  

    662.   

    663.         return realpath($daemonFile);  

    664.     }  

    665.       

    666.     /** 

    667.      * get Freebsd ProcExe 

    668.      * 

    669.      * @param  int $pid 

    670.      * @return string 

    671.      */  

    672.     private function _getFreebsdProcExe($pid)  

    673.     {  

    674.         $strProcExeFile = "/proc/" . $pid . "/file";  

    675.         if (false === ($strLink = @readlink($strProcExeFile))) {  

    676.             //throw new KalonDaemonException("Cannot read link file {$strProcExeFile}");  

    677.             return false;     

    678.         }  

    679.           

    680.         return $strLink;  

    681.     }  

    682.       

    683.     /** 

    684.      * get Linux Proc Exe 

    685.      * 

    686.      * @param  int    $pid 

    687.      * @return string 

    688.      */  

    689.     private function _getLinuxProcExe($pid)  

    690.     {  

    691.         $strProcExeFile = "/proc/" . $pid . "/exe";  

    692.         if (false === ($strLink = @readlink($strProcExeFile))) {  

    693.            //throw new KalonDaemonException("Cannot read link file {$strProcExeFile}");  

    694.             return false;   

    695.         }  

    696.           

    697.         return $strLink;  

    698.     }     

    699.       

    700.     /** 

    701.      * get Freebsd Proc Args 

    702.      * 

    703.      * @param   int    $pid 

    704.      * @return  string 

    705.      */  

    706.     private function _getFreebsdProcArgs($pid)  

    707.     {  

    708.         return $this->_getLinuxProcArgs($pid);  

    709.     }  

    710.       

    711.     /** 

    712.      * get Linux Proc Args 

    713.      * 

    714.      * @param   int  $pid 

    715.      * @return  string 

    716.      */  

    717.     private function _getLinuxProcArgs($pid)  

    718.     {  

    719.         $strProcCmdlineFile = "/proc/" . $pid . "/cmdline";  

    720.           

    721.         if (!$fp = @fopen($strProcCmdlineFile"r")) {  

    722.             throw new KalonDaemonException("Cannot open file {$strProcCmdlineFile} for read");  

    723.                   

    724.         }  

    725.         if (!$strContents = fread($fp, 4096)) {  

    726.              throw new KalonDaemonException("Cannot read or empty file {$strProcCmdlineFile}");   

    727.         }  

    728.         fclose($fp);  

    729.           

    730.         $strContents = preg_replace("/[^/w/.///-]/"" "  

    731.             , trim($strContents));  

    732.         $strContents = preg_replace("//s+/"" "$strContents);  

    733.           

    734.         $arrTemp = explode(" "$strContents);  

    735.         if(count($arrTemp) < 2) {  

    736.             throw new KalonDaemonException("Invalid content in {$strProcCmdlineFile}");   

    737.         }  

    738.           

    739.         return trim($arrTemp[1]);  

    740.     }  

    741.       

    742.     /** 

    743.      * get Linux Proc Cwd 

    744.      * 

    745.      * @param   int    $pid 

    746.      * @return  string 

    747.      */  

    748.     private function _getLinuxProcCwd($pid)  

    749.     {  

    750.         $strProcExeFile = "/proc/" . $pid . "/cwd";  

    751.         if (false === ($strLink = @readlink($strProcExeFile))) {  

    752.             throw new KalonDaemonException("Cannot read link file {$strProcExeFile}");    

    753.         }  

    754.           

    755.         return $strLink;  

    756.     }  

    757.       

    758.     /** 

    759.      * out put process info 

    760.      *   if open _openOutput 

    761.      *  

    762.      * @param  string $str 

    763.      * @return boolean 

    764.      */  

    765.     private function _out($str)  

    766.     {  

    767.         if ($this->_verbose) {  

    768.             fwrite(STDOUT, $str . "/n");  

    769.         }   

    770.         return true;      

    771.     }  

    772.       

    773. }  

    774.   

    775. /** 

    776.  * Exception for KalonDaemon 

    777.  */  

    778. class KalonDaemonException extends Exception   

    779. {  

    780.       

    781. }  

    782. ?>  


    上一篇:PHP CLI模式下的多进程应用
    下一篇:七牛谈分布式存储的元数据设计