찾다
php教程php手册如何写php守护进程(Daemon),守护进程daemon

如何写php守护进程(Daemon),守护进程daemon

守护进程(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 信号说明列表
三、实例

<&#63;php 
* 后台脚本控制类 
*/ 
class DaemonCommand{ 
  
  private $info_dir="/tmp"; 
  private $pid_file=""; 
  private $terminate=false; //是否中断 
  private $workers_count=0; 
  private $gc_enabled=null; 
  private $workers_max=8; //最多运行8个进程 
  
  public function __construct($is_sington=false,$user='nobody',$output="/dev/null"){ 
  
      $this->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"; 
} 
&#63;> 

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

您可能感兴趣的文章:

  • php守护进程 加linux命令nohup实现任务每秒执行一次
  • PHP程序级守护进程的实现与优化的使用概述
  • PHP实现多进程并行操作的详解(可做守护进程)
  • shell脚本作为保证PHP脚本不挂掉的守护进程实例分享
  • PHP高级编程实例:编写守护进程
  • PHP守护进程实例
  • PHP将进程作为守护进程的方法
  • PHP扩展程序实现守护进程
  • 分享PHP守护进程类
성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
php怎么把负数转为正整数php怎么把负数转为正整数Apr 19, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace("&nbsp;","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php怎么判断有没有小数点php怎么判断有没有小数点Apr 20, 2022 pm 08:12 PM

php判断有没有小数点的方法:1、使用“strpos(数字字符串,'.')”语法,如果返回小数点在字符串中第一次出现的位置,则有小数点;2、使用“strrpos(数字字符串,'.')”语句,如果返回小数点在字符串中最后一次出现的位置,则有。

php怎么读取字符串后几个字符php怎么读取字符串后几个字符Apr 22, 2022 pm 08:31 PM

在php中,可以使用substr()函数来读取字符串后几个字符,只需要将该函数的第二个参数设置为负值,第三个参数省略即可;语法为“substr(字符串,-n)”,表示读取从字符串结尾处向前数第n个字符开始,直到字符串结尾的全部字符。

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

WebStorm Mac 버전

WebStorm Mac 버전

유용한 JavaScript 개발 도구

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

DVWA

DVWA

DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경