PHP 编写守护进程

比特启航
• 阅读 186

PHP 编写守护进程

PHP 创建守护进程

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

简介

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

创建步骤

创建子进程,终止父进程

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

在子进程中创建新会话

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

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

改变工作目录

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

重设文件创建掩码

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

关闭文件描述符

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

直接上代码

注:运行环境是 linux 系统,并且要在 cli 模式下运行。
文件名:deamon.php
<?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 编写守护进程

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
梦
4年前
微信小程序new Date()转换时间异常问题
微信小程序苹果手机页面上显示时间异常,安卓机正常问题image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b691e1230e2f15efbd81fe11ef734d4f.png)错误代码vardate'2021030617:00:00'vardateT
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Stella981 Stella981
3年前
Linux查找所有正在运行的守护进程(daemon)
pseoppid,pid,sid,stat,tty,comm |awk'{if($2$3&&$5"?"){print$0};}'首先,要注意,守护进程(daemon)和后台进程(backgroundprocess)有区别。守护进程是一种后台进程,但是,同时,它必须具备以下特性:1\.没
CRM从哪些方面进行了管理?
我们将CRM(https://www.sap.cn/products/crm.html!image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/17e2d96568a98f0