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.

浙公网安备 33010602011771号