侯体宗的博客
  • 首页
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

如何写php守护进程(Daemon)

php  /  管理员 发布于 7年前   263

守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。php也可以实现守护进程的功能。
一、基本概念
进程: 每个进程都有一个父进程,子进程退出,父进程能得到子进程退出的状态。
进程组:每个进程都属于一个进程组,每个进程组都有一个进程组号,该号等于该进程组组长的PID
二、守护编程要点
1. 在后台运行     
         为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。 if($pid=pcntl_fork()) exit(0);//是父进程,结束父进程,子进程继续
2. 脱离控制终端,登录会话和进程组 
       有必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终  端。 控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长: posix_setsid();
        说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
3. 禁止进程重新打开控制终端
        现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端: if($pid=pcntl_fork()) exit(0);//结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
4. 关闭打开的文件描述符
        进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
        fclose(STDIN),fclose(STDOUT),fclose(STDERR)关闭标准输入输出与错误显示。
5. 改变当前工作目录
        进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如chdir("/")
6. 重设文件创建掩模
        进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0);
7. 处理SIGCHLD信号
        处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影  响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。 signal(SIGCHLD,SIG_IGN);
        这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。关于信号的问题请参考Linux 信号说明列表
三、实例

is_sington=$is_sington; //是否单例运行,单例运行会在tmp目录下建立一个唯一的PID       $this->user=$user;//设置运行的用户 默认情况下nobody       $this->output=$output; //设置输出的地方       $this->checkPcntl();   }   //检查环境是否支持pcntl支持   public function checkPcntl(){     if ( ! function_exists('pcntl_signal_dispatch')) {       // PHP < 5.3 uses ticks to handle signals instead of pcntl_signal_dispatch       // call sighandler only every 10 ticks       declare(ticks = 10);     }       // Make sure PHP has support for pcntl     if ( ! function_exists('pcntl_signal')) {       $message = 'PHP does not appear to be compiled with the PCNTL extension. This is neccesary for daemonization';       $this->_log($message);       throw new Exception($message);     }     //信号处理     pcntl_signal(SIGTERM, array(__CLASS__, "signalHandler"),false);     pcntl_signal(SIGINT, array(__CLASS__, "signalHandler"),false);     pcntl_signal(SIGQUIT, array(__CLASS__, "signalHandler"),false);       // Enable PHP 5.3 garbage collection     if (function_exists('gc_enable'))     {       gc_enable();       $this->gc_enabled = gc_enabled();     }   }     // daemon化程序   public function daemonize(){       global $stdin, $stdout, $stderr;     global $argv;       set_time_limit(0);       // 只允许在cli下面运行     if (php_sapi_name() != "cli"){       die("only run in command line mode\n");     }       // 只能单例运行     if ($this->is_sington==true){         $this->pid_file = $this->info_dir . "/" .__CLASS__ . "_" . substr(basename($argv[0]), 0, -4) . ".pid";       $this->checkPidfile();     }       umask(0); //把文件掩码清0       if (pcntl_fork() != 0){ //是父进程,父进程退出       exit();     }       posix_setsid();//设置新会话组长,脱离终端       if (pcntl_fork() != 0){ //是第一子进程,结束第一子进程         exit();     }       chdir("/"); //改变工作目录       $this->setUser($this->user) or die("cannot change owner");       //关闭打开的文件描述符     fclose(STDIN);     fclose(STDOUT);     fclose(STDERR);       $stdin = fopen($this->output, 'r');     $stdout = fopen($this->output, 'a');     $stderr = fopen($this->output, 'a');       if ($this->is_sington==true){       $this->createPidfile();     }     }   //--检测pid是否已经存在   public function checkPidfile(){       if (!file_exists($this->pid_file)){       return true;     }     $pid = file_get_contents($this->pid_file);     $pid = intval($pid);     if ($pid > 0 && posix_kill($pid, 0)){       $this->_log("the daemon process is already started");     }     else {       $this->_log("the daemon proces end abnormally, please check pidfile " . $this->pid_file);     }     exit(1);     }   //----创建pid   public function createPidfile(){       if (!is_dir($this->info_dir)){       mkdir($this->info_dir);     }     $fp = fopen($this->pid_file, 'w') or die("cannot create pid file");     fwrite($fp, posix_getpid());     fclose($fp);     $this->_log("create pid file " . $this->pid_file);   }     //设置运行的用户   public function setUser($name){       $result = false;     if (empty($name)){       return true;     }     $user = posix_getpwnam($name);     if ($user) {       $uid = $user['uid'];       $gid = $user['gid'];       $result = posix_setuid($uid);       posix_setgid($gid);     }     return $result;     }   //信号处理函数   public function signalHandler($signo){       switch($signo){         //用户自定义信号       case SIGUSR1: //busy       if ($this->workers_count < $this->workers_max){         $pid = pcntl_fork();         if ($pid > 0){           $this->workers_count ++;         }       }       break;       //子进程结束信号       case SIGCHLD:         while(($pid=pcntl_waitpid(-1, $status, WNOHANG)) > 0){           $this->workers_count --;         }       break;       //中断进程       case SIGTERM:       case SIGHUP:       case SIGQUIT:           $this->terminate = true;       break;       default:       return false;     }     }   /**   *开始开启进程   *$count 准备开启的进程数   */   public function start($count=1){       $this->_log("daemon process is running now");     pcntl_signal(SIGCHLD, array(__CLASS__, "signalHandler"),false); // if worker die, minus children num     while (true) {       if (function_exists('pcntl_signal_dispatch')){           pcntl_signal_dispatch();       }         if ($this->terminate){         break;       }       $pid=-1;       if($this->workers_count<$count){           $pid=pcntl_fork();       }         if($pid>0){           $this->workers_count++;         }elseif($pid==0){           // 这个符号表示恢复系统对信号的默认处理         pcntl_signal(SIGTERM, SIG_DFL);         pcntl_signal(SIGCHLD, SIG_DFL);         if(!empty($this->jobs)){           while($this->jobs['runtime']){ if(empty($this->jobs['argv'])){   call_user_func($this->jobs['function'],$this->jobs['argv']); }else{   call_user_func($this->jobs['function']); } $this->jobs['runtime']--; sleep(2);           }           exit();           }         return;         }else{           sleep(2);       }         }       $this->mainQuit();     exit(0);     }     //整个进程退出   public function mainQuit(){       if (file_exists($this->pid_file)){       unlink($this->pid_file);       $this->_log("delete pid file " . $this->pid_file);     }     $this->_log("daemon process exit now");     posix_kill(0, SIGKILL);     exit(0);   }     // 添加工作实例,目前只支持单个job工作   public function setJobs($jobs=array()){       if(!isset($jobs['argv'])||empty($jobs['argv'])){         $jobs['argv']="";       }     if(!isset($jobs['runtime'])||empty($jobs['runtime'])){         $jobs['runtime']=1;       }       if(!isset($jobs['function'])||empty($jobs['function'])){         $this->log("你必须添加运行的函数!");     }       $this->jobs=$jobs;     }   //日志处理   private function _log($message){     printf("%s\t%d\t%d\t%s\n", date("c"), posix_getpid(), posix_getppid(), $message);   }   }   //调用方法1 $daemon=new DaemonCommand(true); $daemon->daemonize(); $daemon->start(2);//开启2个子进程工作 work();         //调用方法2 $daemon=new DaemonCommand(true); $daemon->daemonize(); $daemon->addJobs(array('function'=>'work','argv'=>'','runtime'=>1000));//function 要运行的函数,argv运行函数的参数,runtime运行的次数 $daemon->start(2);//开启2个子进程工作   //具体功能的实现 function work(){    echo "测试1"; } ?> 

以上就是关于php守护进程的相关介绍,希望对大家的学习有所帮助。

您可能感兴趣的文章:

  • php守护进程 加linux命令nohup实现任务每秒执行一次
  • PHP高级编程实例:编写守护进程
  • shell脚本作为保证PHP脚本不挂掉的守护进程实例分享
  • PHP将进程作为守护进程的方法
  • PHP扩展程序实现守护进程
  • PHP实现多进程并行操作的详解(可做守护进程)
  • PHP守护进程的两种常见实现方式详解
  • PHP程序员玩转Linux系列 使用supervisor实现守护进程
  • PHP程序级守护进程的实现与优化的使用概述
  • php脚本守护进程原理与实现方法详解
  • PHP守护进程化在C和PHP环境下的实现
  • php实现简单的守护进程创建、开启与关闭操作


  • 上一条:
    分享PHP守护进程类
    下一条:
    PHP汉字转换拼音的函数代码
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 用Time Warden监控PHP中的代码处理时间(0个评论)
    • 在PHP中使用array_pop + yield实现读取超大型目录功能示例(0个评论)
    • Property Hooks RFC在PHP 8.4中越来越接近现实(0个评论)
    • 近期文章
    • 在windows10中升级go版本至1.24后LiteIDE的Ctrl+左击无法跳转问题解决方案(0个评论)
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(95个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-11
    • 2017-12
    • 2018-01
    • 2018-02
    • 2018-03
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-09
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-09
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客