thinkingbullet 发表于 2013-7-11 16:48:51

关于守护进程的几个百撕不得骑姐的问题[希望管理员顶起...

先说一下我的需求:服务器产生一个常驻进程,此进程用来运行我们的php脚本,在脚本中我们会用到pcntl系列方法,而且我们希望每次fork N个子进程,多余的子进程放进一个队列里(数组队列),当N个子进程运行结束的时候在运行队列中的N个子进程,以此类推,直到全部进程结束.
下面是我们官方手册上找的一个NB的列子,顺便写上我做的注释(可能翻译的很狗血),我先把自己不懂得提出来
1:脚本开始就用declare(ticks=1); 目的何为,(google了一下,发现说的都很模糊,英文又看不懂,我哭了...),注释了后程序一直处于等待状态算是一个死循环吧.
2:出现的两个sleep(),其中一个或者全部注释后程序运行和情况一相同,为什么sleep后(或者sleep时)程序貌似进入另一种循环状态(这点我说不清楚,因为我看不出来他是怎么调用launchJob方法或者是run方法)
3:这个程序没有执行childSignalHandler这个方法,那他是如何解决N个子进程结束后在执行另外N个子进程
4:请大牛把这个例子的执行流程清晰的写下来(万分感谢啊{:soso_e105:}),并把关键点和需要额外补充的知识点说一下(还是万分感谢啊{:soso_e105:})

<?php
/**
* Using pcntl_fork() can be a little tricky in some situations.For fast jobs, a child can finish processing before the parent process has executed some code related to
*the launching of the process.The parent can receive a signal before it's ready to handle the child process' status.To handle this scenario, I add an id to a
* "queue" of processes in the signal handler that need to be cleaned up if the parent process is not yet ready to handle them.
* I am including a stripped down version of a job daemon that should get a person on the right track.
*
* @author Sammy Guergachi <sguergachi at gmail.com>
*/



declare(ticks=1);
//A very basic job daemon that you can extend to your needs.
class JobDaemon{

    public $maxProcesses = 2;
    protected $jobsStarted = 0;
    protected $currentJobs = array();
    protected $signalQueue=array();   
    protected $parentPID;

    public function __construct(){
      echo "constructed \n";
      $this->parentPID = getmypid();
      pcntl_signal(SIGCHLD, array($this, "childSignalHandler"));
    }

    /**
    * Run the Daemon
    * 运行守护进程
    */
    public function run(){
      echo "Running \n";
      for($i=0; $i<10; $i++){
            $jobID = rand(0,10000000000000);

            while(count($this->currentJobs) >= $this->maxProcesses){
               echo "Maximum children allowed, waiting...\n";
               sleep(1);
            }

            $launched = $this->launchJob($jobID);
      }

      //Wait for child processes to finish before exiting here
      //等待子进程完成之前离开这里
      while(count($this->currentJobs)){
            echo "Waiting for current jobs to finish... \n";
            sleep(1);
      }
    }

    /**
    * Launch a job from the job queue
    * 从作业(任务)队列启动工作
    */
    protected function launchJob($jobID){
      //创建子进程
      $pid = pcntl_fork();
      if($pid == -1){
            //Problem launching the job
            //启动作业出问题了
            error_log('Could not launch new job, exiting');
            return false;
      }else if ($pid){
            // Parent process
            // Sometimes you can receive a signal to the childSignalHandler function before this code executes if
            // the child script executes quickly enough!
            // 父进程
            //如果子脚本执行不够快.在代码执行前,有时候你会从childSignalHandler函数中接收一个信号
            $this->currentJobs[$pid] = $jobID;

            // In the event that a signal for this pid was caught before we get here, it will be in our signalQueue array
            // So let's go ahead and process it now as if we'd just received the signal
            //在这个pid的信号被捕捉之前我们到达,它将在我们signalQueue数组中
            //因此让我们继续前进并处理他,如果我们刚刚收到信号
            if(isset($this->signalQueue[$pid])){
                echo "found $pid in the signal queue, processing it now \n";
                $this->childSignalHandler(SIGCHLD, $pid, $this->signalQueue[$pid]);
                unset($this->signalQueue[$pid]);
            }
      }else{
            //Forked child, do your deeds....
            //forked的子进程,做你想做的
            $exitStatus = 0; //Error code if you need to or whatever 如果你需要或任何的错误代码
            echo "Doing something fun in pid ".getmypid()."\n";
            exit($exitStatus);
      }
      return true;
    }

    public function childSignalHandler($signo, $pid=null, $status=null){

      //If no pid is provided, that means we're getting the signal from the system.Let's figure out
      //which child process ended
      //如果没有pid被提供,这意味着我们从系统得到信号,让我们弄清楚哪个子进程结束了
      if(!$pid){
            //等待或返回fork的子进程状态
            $pid = pcntl_waitpid(-1, $status, WNOHANG);
      }

      //Make sure we get all of the exited children
      //确保我们得到所有已退出的子进程
      while($pid > 0){
            if($pid && isset($this->currentJobs[$pid])){
                //返回一个中断的子进程的返回代码
                $exitCode = pcntl_wexitstatus($status);
                if($exitCode != 0){
                  echo "$pid exited with status ".$exitCode."\n";
                }
                unset($this->currentJobs[$pid]);
            }
            else if($pid){
                //Oh no, our job has finished before this parent process could even note that it had been launched!
                //Let's make note of it and handle it when the parent process is ready for it
                //我们的工作已完成,在此之前的父进程甚至可能会注意到,它已启动!
                //注意,当父进程准备好时处理它
                echo "..... Adding $pid to the signal queue ..... \n";
                $this->signalQueue[$pid] = $status;
            }
            $pid = pcntl_waitpid(-1, $status, WNOHANG);
      }
      return true;
    }
}
$daemon = new JobDaemon();
$daemon -> run();
?>

原例子:http://www.php.net/manual/en/function.pcntl-fork.php#98711

thinkingbullet 发表于 2013-7-11 16:49:26

我先顶起一下,大家把此贴顶起来啊,让更多的人看到.

thinkingbullet 发表于 2013-7-11 20:07:05

为什么没有人回答呢,难道我的问题很幼稚吗,跪求高人指点啊

三年二班 发表于 2015-1-6 12:58:23

存数据库就简单多了,加个执行完的时间就行了,
页: [1]
查看完整版本: 关于守护进程的几个百撕不得骑姐的问题[希望管理员顶起...