时间:2021-07-01 10:21:17 帮助过:34人阅读
<?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"; } ?>
以上就是关于php守护进程的相关介绍,希望对大家的学习有所帮助。