rokaye

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

PHP脚本写后台守护进程

关于后台守护进程的介绍和实现规则,请看《Linux守护进程》

PHP的PCNTL和POSIX扩展函数,如:pcntl_fork(),posix_setsid(),posix_getpid(),posix_getsid(),posix_kill()等,可以方便写后台守护程序.
根据Deamon实现规则,使用PHP实现deamon的步骤如下:

// 1) Fork and exit the parent.
$pid = pcntl_fork();
if ($pid == -1) {
  die("could not fork"); 
}
else if ($pid) {
  exit();  # Kill the parent 
}

// 2) become session leader, pg leader, no term
$session_id = posix_setsid();
if (!$session_id) {
  die("Could not detach from terminal.");
}

// 3) cd to /
if (!chdir('/')) { die("Could not cd to rootfs"); }

// 4) set file creation mask to 0
$oldmask = umask(00);

//  5) Close unneeded file handles
foreach (array(STDIN,STDOUT,STDERR) as $fh) {
  if (!fclose($fh)) { die("Unable to close $fh"); }
}

// 6) Set up signal handlers where necessary.
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");

function sig_handler($signo) {
  switch ($signo) {
    case SIGTERM:
      //handle shutdown tasks
      die("Caught thud SIGTERM");
      break;
    case SIGHUP:
      //handle restart tasks
      die("Caught thud SIGHUP");
      break;
    default:
      //handle all other signals
   }
}

再看看下面一个完整实现的例子,每隔10s打印出“this is a deamon“


<?php
// run.php
class ServiceDeamon {
	protected $pidfile;
	protected $logfile;
	protected $errorfile;
	protected $basedir;
	protected $tasks = array();

	public function __construct($pidfile = null) {
		$this->basedir = __DIR__;
		$this->logfile = $this->basedir . '/serviceDeamon.log';
		$this->errorfile = $this->basedir . '/serviceDeamon.err';
		if ($pidfile === null) {
			$this->pidfile = $this->basedir . '/serviceDeamon.pid';
		} else {
			$this->pidfile = $pidfile;
		}
	}

	public function deamonize() {
		/// fork and exit the parent
		$pid = pcntl_fork();
		if ($pid < 0 ) {
			die('could not fork');
		} else if ($pid > 0) {
			exit();  // kill the parent process
		}

	    // detach from the terminal and become session leader
	    if (posix_setsid() === -1) {
	  	    die('could not detach from terminal');
	    }
	    // log current deamon process id
	    file_put_contents($this->pidfile, posix_getpid());
	   
		foreach ($this->tasks as $task) {
	    	$task();
	    }
	}

	public function setLog($logfile) {
		$this->logfile = $logfile;
		return $this;
	} 

	public function setErorr($errorfile) {
		$this->errorfile = $errorfile;
		return $this;
	}

	public function getPid() {
		if (file_exists($this->pidfile)) {
			$pid = (int) file_get_contents($this->pidfile);
			if (posix_kill($pid, SIG_DFL)) {
				return $pid;
			} else {
				unlink($this->pidfile);
				return 0;
			}
		} else {
			return 0;
		}
	}

	public function start() {
		if ( ($pid = $this->getPid()) > 0 ) {
			echo "Process is running on PID: " . $pid . PHP_EOL;
		} else {
			echo "Starting ..." . PHP_EOL;
			$this->deamonize();
		}
	}

    public function stop() {
		if ( ($pid = $this->getPid()) > 0) {
			echo "Stopping..." . PHP_EOL;
			posix_kill($pid, SIGTERM);
			unlink($this->pidfile);
		} else {
			echo "Process not running yet!" . PHP_EOL;
		}
	}

	public function reload() {
		$this->stop();
		$this->start();
	}

	public function status() {
		if ( ($pid = $this->getPid()) > 0 ) {
			echo "Process is running on PID: " . $pid . PHP_EOL;
		} else {
			echo "Process not running yet!" . PHP_EOL;
		} 
	}

	public function run($argv) {
		$param = is_array($argv) && count($argv) == 2 ? $argv[1] : null;
		switch ($param) {
			case 'start':
                $this->start();
            break;
            case 'stop':
                $this->stop();
            break;
            case 'reload':
                $this->reload();
            break;
            case 'status':
                $this->status();
            break;
            default:
            	echo "Unknown command!" . PHP_EOL .
            	"Usage: " . $argv[0] . " start|stop|reload|status" . PHP_EOL;
            break;
		}
	}

	public function addService($servicename, callable $servicecallback) {
		$this->tasks[$servicename] = Closure::bind($servicecallback, $this, get_class());
	}
}

// add your own task
$serviceDeamon = new ServiceDeamon();
$serviceDeamon->addService('test', function(){
	$i = 0;
	while(true) {
		++$i;
		echo $i . ': this is a deamon' . PHP_EOL;
		sleep(10);
	}
});
$serviceDeamon->run($argv);

To start:
localhost:~$ php run.php start

To stop:
localhost:~$ php run.php stop

To check the status:
localhost:~$ php run.php status

To reload:
localhost:~$ php run.php reload

Posted in Daemon / Worker.

posted on 2015-05-22 14:16  rokaye  阅读(944)  评论(0)    收藏  举报