php 如何守护进程_PHP 编写守护进程

PHP 创建守护进程进程根据状态可以分为三种进程,守护进程,僵尸进程,孤儿进程。今天我们着重来分析下守护进程。

简介

守护进程 (daemon) 是一类在后台运行的特殊进程,用于执行特定的系统任务。很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。另一些只在需要的时候才启动,完成任务后就自动结束。

创建步骤

创建子进程,终止父进程

由于守护进程是脱离控制终端的,因此首先创建子进程,终止父进程,使得程序在shell 终端里造成一个已经运行完毕的假象。之后所有的工作都在子进程中完成,而用户在 shell 终端里则可以执行其他的命令,从而使得程序以僵尸进程形式运行,在形式 上做到了与控制终端的脱离。

在子进程中创建新会话

这个步骤是创建守护进程中最重要的一步,在这里使用的是系统函数setsid。setsid 函数用于创建一个新的会话,并担任该会话组的组长。调用 setsid 的三个作用:让进程摆脱原会话的控制、让进程摆脱原进程组的控制和让进程摆脱原控制终端的控制。

在调用 fork 函数时,子进程全盘拷贝父进程的会话期 (session,是一个或多个进程组的集合)、进程组、控制终端等,虽然父进程退出了,但原先的会话期、进程组、控制终端等并没有改变,因此,那还不是真正意义上使两者独立开来。setsid 函数能够使进程完全独立出来,从而脱离所有其他进程的控制。

改变工作目录

使用fork 创建的子进程也继承了父进程的当前工作目录。由于在进程运行过程中,当前目录所在的文件系统不能卸载,因此,把当前工作目录换成其他的路径,如 “/” 或 “/tmp” 等。改变工作目录的常见函数是 chdir。

重设文件创建掩码

文件创建掩码是指屏蔽掉文件创建时的对应位。由于使用fork 函数新建的子进程继承了父进程的文件创建掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件创建掩码设置为 0,可以大大增强该守护进程的灵活性。设置文件创建掩码的函数是 umask,通常的使用方法为 umask (0)。

关闭文件描述符

用fork 新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读或写,但它们一样消耗系统资源,可能导致所在的文件系统无法卸载。

直接上代码

注:运行环境是 linux 系统,并且要在 cli 模式下运行。

文件名:deamon.php

/**

* User: streetlamp

* Date: 2019/1/9

* Time: 15:14

*/

class Deamon{

protected $_pidFile;

public function __construct(){

$this->_pidFile = '/var/www/html/queue/public/pid.log';

$this->_checkPcntl();

}

/**

* 创建守护进程核心函数

* @return string|void

*/

private function _demonize(){

if (php_sapi_name() != 'cli') {

die('Should run in CLI');

}

//创建子进程

$pid = pcntl_fork();

if ($pid == -1) {

return 'fork faile';

} elseif ($pid) {

//终止父进程

exit('parent process');

}

//在子进程中创建新的会话

if (posix_setsid() === -1) {

die('Could not detach');

}

//改变工作目录

chdir('/');

//重设文件创建的掩码

umask(0);

$fp = fopen($this->_pidFile, 'w') or die("Can't create pid file");

//把当前进程的id写入到文件中

fwrite($fp, posix_getpid());

fclose($fp);

//关闭文件描述符

fclose(STDIN);

fclose(STDOUT);

fclose(STDERR);

//运行守护进程的逻辑

$this->job();

return;

}

/**

* 守护进程的任务

*/

private function job(){

//TODO 你的守护经常需要执行的任务

while (true) {

file_put_contents('/var/www/html/queue/public/job.log', 'job' . PHP_EOL, FILE_APPEND);

sleep(5);

}

}

/**

* 获取守护进程的id

* @return int

*/

private function _getPid(){

//判断存放守护进程id的文件是否存在

if (!file_exists($this->_pidFile)) {

return 0;

}

$pid = intval(file_get_contents($this->_pidFile));

if (posix_kill($pid, SIG_DFL)) {//判断该进程是否正常运行中

return $pid;

} else {

unlink($this->_pidFile);

return 0;

}

}

/**

* 判断pcntl拓展

*/

private function _checkPcntl(){

!function_exists('pcntl_signal') && die('Error:Need PHP Pcntl extension!');

}

private function _message($message){

printf("%s %d %d %s" . PHP_EOL, date("Y-m-d H:i:s"), posix_getpid(), posix_getppid(), $message);

}

/**

* 开启守护进程

*/

private function start(){

if ($this->_getPid() > 0) {

$this->_message('Running');

} else {

$this->_demonize();

$this->_message('Start');

}

}

/**

* 停止守护进程

*/

private function stop(){

$pid = $this->_getPid();

if ($pid > 0) {

//通过向进程id发送终止信号来停止进程

posix_kill($pid, SIGTERM);

unlink($this->_pidFile);

echo 'Stoped' . PHP_EOL;

} else {

echo "Not Running" . PHP_EOL;

}

}

private function status(){

if ($this->_getPid() > 0) {

$this->_message('Is Running');

} else {

echo 'Not Running' . 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 'status':

$this->status();

break;

default:

echo "Argv start|stop|status " . PHP_EOL;

break;

}

}

}

$deamon = new \Deamon();

$deamon->run($argv);复制代码

运行方式开启守护进程:php demon.php start

停止守护进程:php demon.php stop

查看守护进程的状态:php demon.php status

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值